Quantcast
Channel: WhiskeyTangoHotel.Com
Viewing all 133 articles
Browse latest View live

The BeagleBone

$
0
0
-----
Thanks to Texas Instruments and BeagleBoard a BeagleBone arrived at the doorstep of The Lab yesterday.  The BeagleBone is an extremely cool piece of kit that is basically a credit card sized Linux computer.  It uses a TI Sitara ARM Cortex processor with plenty of I/O.  The on board SD card comes preloaded with the OS and after a short five minutes (using the included USB cable) I had the BeagleBone establish a connection with my Mac and observed the "all is Okay" heart beat pattern on one of the user LEDs.

I'm pretty excited about the BeagleBone; it seems crazy powerful, and plan to use it to establish world domination.  Stay tuned!
-----


Electric Imp Tweeting Cat Door

$
0
0
Objective:
  • Using the Electric Imp,  create a rig to 'tweet' whenever activity is sensed on a cat door.
  • Create a real time graph of the activity here.
For those not interested in the build details, Kelso helped with this short video of @OurCatDoor in action:
    -----
    We have two cats (Zena and Kelso) that were eager to get online.  When they are not helping out in the labs (example pic) they like to walk through a cat door and relax on the screened in sun porch where they can enjoy food, drink, and take a bathroom break.

    Tweeting cat doors are nothing new to The Net.  However, when I saw the Electric Imp on Sparkfun's weekly product video and thought it could be a different way to tackle the problem and learn a few things along the way.  Let's take you through the process.

    To start you are going to need an Electric Imp and an "April" Imp Breakout Board.  Oh, and a cat or two...



    The Electric Imp is a pretty cool piece of kit.  It looks like a SD Card, but it is really a Cortex-M3 processor with WiFi on board.  Damn!  An onboard mini USB connector is used only to power the device; not for data.
    -----
    The Electric Imp is useless until you get it established with a wireless network.  As mentioned above, the USB is not used for data.  So how do you program it?  With light from the screen of an app that is run on your iPhone or Android.  You enter your WiFi SSID (and security paswords if needed) and the app sends "flashes" to the screen that are picked up by the Imp to program it.  The process works surprisingly well.
    ----
    The physical I/O for the project is pretty straight forward.  All that is needed is a magnetic reed switch to sense if the cat door flap has opened.  A blue LED is used as a visual clue for ON/OFF and switch detection.  After deciding how to map the Imp's GPIO for the project, the schematic is embarrassingly simple:
    -----
    After soldering headers to the April breakout board a connector was repurposed making the connection setup clean and simple.
    -----
    The real challenge is in the software and the cross platform integration (Imp to WhiskeyTangoHotel.com webserver to Sen.se to Twitter) and that process was not trivial, but I'll take you through that as well.
    -----
    Now that you have the Imp all wired up and connected to a WiFi network, how do you program it?  Remember again that the USB is just for power.  The Imp is programmed in Squirrel through your browser (I used Chrome) from the Electric Imp website.  To me, Squirrel has a "C" type feel to it.  You work through a screen called "The Planner" to code up your project behind "nodes".  These "nodes" can be connected with "noodles" for flow control.  Here is the Imp planner screenshot of my finished project.  Note that you can't see the source code here as it is embedded under the "blue node".  I'll show my source code below later.
    -----
    In The Planner above, the Imp runs the code under the BLUE box and passes output to the GREEN box.  The code in the BLUE box has the Imp doing a few basic things:  1) turn on a blue LED to let everyone know the Imp is powered and 2) monitoring a magnetic reed switch on the cat door.  If the cat door is opened the reed switch opens contact.  The Imp will 'see' the switch change and 3) blink the blue LED off to provide a visual confirmation and 4) pass the text string to be "tweeted" to the green box.

    So what's in the green box?  When the green box is called a PHP script is executed on the WhiskeyTangoHotel.com webserver.  From this point, we leave the Electric Imp world and enter the Sen.se world.  
    -----
    Sen.se is one cool place.  Their goal is to assist in internet connectivity of personal devices.  They have widgets, tools, applications, channels and a few other things that I barely understand. One of the widgets found there helps with broadcasting to Twitter.  So, the PHP script in the green box above passes the string the cat wanted to "tweet" to the Sen.se Twitter app and the final result is achieved; a cat walking through a door unknowingly Tweeting a message to the world.  

    Presto!  Even without opposable thumbs, the ability to type, speak English, read, or boot a computer our cats are online.  As a bonus, we publicly display a real time graph of the activity here.
    -----
    A few closing comments... I found the current Electric Imp user community to be pretty small. That said, the project wouldn't have come off without help from Electric Imp forums.  Most notable are "brendandawes" and "Hugo" who were my two main enablers.  Hugo is actually the CEO/Co-Founder of Electric Imp and I was pretty impressed that he is so involved.  That said, I do wonder how the project would have gone with a near cost equivalent Raspberry Pi.  I never really got comfortable with the fact that all my Electric Imp code was not stored locally.  I also wish there were more examples on the web to build a knowledge base from.   But, the Electric Imp package is crazy small and tight with low power draw so that is a huge plus.  In all I can't complain, the project works great and taught me a lot.  I don't have any experience with the Raspberry Pi anyway, so any judgement will have to wait. 
    -----
    If you are still with us, here is some source code for your reading pleasure:
    -----
    Code Under the "Blue" Box Electric Imp Planner

    /*
    Tweeting Cat Door
    Dec 2012
    www.WhiskeyTangoHotel.Com

    Read switch on PIN7 and GND
        Normally Open or Normally Closed can be adjusted in swEvent function
    Blue LED PIN9 to V3V is ON if the code in running
        Blinks off for 0.5 secs when door switch dectected
    */

    hardware.pin9.write(0); // LED ON, active LOW
    local channelOutput = [ OutputPort("Ch 1", "string")]; // String var that gets sent to PHP script

    //Watchdog code below send Ping to Imp server every xx secs
    //I added this because the Imp was not Tweeting all switch closures and
    //power up/down seems to correct it.  Assuming lost cost with Imp server?
    function watchdog() {
      imp.wakeup(120,watchdog);  //xxx is seconds between pings
      server.log("WD ping");
    }
    watchdog();
    //End of my watchdog code experiment

    // function swEvent handles looking for action on the door switch
    function swEvent() {    
            local d = date();  // Date info not sent to twitter.  Used only for planner debug
            local min = d["min"];
            local hour = d["hour"];
            local sec = d["sec"];
            local state = hardware.pin7.read();
            if (state == 0) {   // "1" Tweets when switch opens (for a NC swtich).  "0" Tweets when switch closes (for a NO swtich).
         
            // Select Random Msg.  Increase %xx as number of random msgs increases
                local r = math.rand()%4;   // generate random number (min val is 0.  max val is %xxx in Decimal)
       
                if (r == 3) {
                    channelOutput[0].set("Random message for r = 3 here");
                }
             
                if (r == 2) {
                    channelOutput[0].set("Random message for r = 2 here");
                }
             
                if (r == 1) {
                    channelOutput[0].set("Random message for r = 1 here");
                }
             
                if (r == 0) {
                    channelOutput[0].set("Random message for r = 0 here.");
                }
            // end Random Msg selecton
         
            // Blink the LED Off to show a door swing detected
            hardware.pin9.write(1);  // LED OFF
            imp.sleep(0.5)          // 500mS delay
            hardware.pin9.write(0);  //  LED back ON
         
            server.show("r="+r+" @ "+hour+":"+min+":"+sec);  //echo to the Imp Planner status the generated RANDOM# and time.  Used for debug.
            }   // end if state ==0 (or state ==1 depending on NO or NC switch type)
           
            imp.sleep(1.0);  // switch settle time of x.x seconds (keep greater that 0.5)    
            server.log(state);  //  echo switch state (O or 1) to planner window for debug      
    }  // End function swEvent

    // Configure pin 9 as an open drain output with internal pull up
    // Configure pin 7 as switch
    hardware.pin9.configure(DIGITAL_OUT_OD_PULLUP);
    hardware.pin7.configure(DIGITAL_IN_PULLUP, swEvent);

    imp.configure( "TCD w-Blink", [], channelOutput);
    ------
    PHP Script called from Green Box in the Electric Imp Planner:
    <?php
    $feed_id = "sen.seAsignedXXX.XXXXXX";
    $api_key = "sen.seAssignedXXX.XXXXXX";
    if(isset($_POST['value'])) {
    $data = array("feed_id" => $feed_id, "value" => $_POST['value']);                                                                    
    $data_string = json_encode($data);                                                                                   
    $ch = curl_init('http://api.sen.se/events/?sense_key='.$api_key);                                                                      
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");                                                                     
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);                                                                  
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);                                                                      
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(                                                                          
        'Content-Type: application/json',                                                                                
        'Content-Length: ' . strlen($data_string))                                                                       
    );                                                                                                                   
    $result = curl_exec($ch);
    }
    ?>
    -----
    Link back: Hack a Day
    Link back: Hacked Gadgets
    -----

    SpiroGraph 3D Laser Project [with Arduino]

    $
    0
    0
    Objective:  Reflect a laser beam off mirrors mounted on three fans to display a 'wild' 3D spirograph type pattern on any surface.  Provide fan speed control (manual or automatic) to adjust the displayed patterns.  Full electrical schematic and source code follows.
    -----
    If you are not interested in the build and just want to see the result, below is a short video.  If you want to really "trip" out you can watch the 20 minute version.  If you turn up the volume you can hear the fans speeds changing.




    -----
    If you read through the site, you can see I have been using other micro controllers (MSP430, PIC, PICAXE, Freescale Freedom, etc.)  The Arduino platform is certainly one of the most popular and I wanted to give it a try.

    I approached this as an Arduino tutorial project because it combines a lot of basics that go into many projects.  I would recommend it to anyone interested in learning the Arduino because:
    • It's cool (maybe even relaxing) to watch.  Be the envy of your friends...
    • The parts are cheap; many probably already in your kit.
    • Demonstrates reading multiple ADC (Analog to Digital) voltage inputs.
    • Demonstrates multiple PWM (Pulse Width Modulation) outputs to vary LED brightness and control motor speeds.
    • Demonstrates random number generation with the Arduino.
    • Demonstrates using arrays for both variables and pin I/O assignments.
    • Demonstrates the Arduino "mapvalue" scaling function.
    • Demonstrates output of Arduino debug values to the PC screen.
    • Demonstrates multiple voltages being used for a project (12VDC, 5VDC, and 3.3VDC).
    • Demonstrates other program control stuff, etc....
    If you are trying to learn the Arduino (or really, any other micro controller) this project beats the hell out of just a "Hello World" blinking LED.  The effect is guaranteed to impress your friends at the next Rave Party.
    ----
    For the project you will need a few items:
    • Arduino (I used the Nano pictured above; $9USD shipped)
    • Laser Diode (check eBay for red ones that sell for ~$1.50USD shipped)
    • Three DC fans or motors (I rescued three 24VDC instrument cooling fans)
    • Three small mirrors and double sided tape to attach then to each fan center
    • Three 10K pots (used to control the fan speed independently)
    • Three LEDs and three 330 ohm resistors
    • AC/DC Power adaptor (I rescued an 18VDC wall wart)
    • LM7805 voltage regulator to tame the output from the wall wart to 5VDC
    • L78L33 voltage regulator (provides a 3.3VDC for the laser diode)
    • TC4469 Quad Motor Driver to provide controlled power to the fans
    -----
    First thing we need to do is arrange the three fan motors in a box pattern.  I fixed them together with yellow zip ties.  The "wall" on the far left is just a fan housing and where we will mount the red laser diode.  There is a better pic of that later.
    -----
    Here is a pic of the red laser diode mounted on a thick wire.  You can also see one of the small mirrors mounted to one of the fan's center with double sided tape.  
    -----
    The red laser beam is aimed so that it reflects off each mirror as it spins, and finally, on onto a wall, etc.  The path of the laser on the spinning mirrors is kinda like this:
    -----
    Next thing you will need to do connect up a bunch of wires per the schematic below.  Since there is voltage on the project that is higher that the Arduino, the LEDs, or the laser can handle pay special attention when connecting the voltage regulators (LM7805 and L78L33) and components or you will "cook" something.  Also, don't try to skimp out and go without the TC4469 to drive the fans.  The Arduino can't source enough current to drive the fans.  Motor drivers are common in projects so this is a good time learn how to use them anyway.   Click on the schematic to make to bigger.
    -----
    After connecting everything up, the mess will look something like this:
    -----
    We still need to program the Arduino Nano to control the project; read the POT locations, adjust the fan speeds and LED brightness, speed change delays, etc.  Simply copy and paste the source code below into the Arduino IDE (Integrated Development Enviroment) installed on your PC.  Then download the source code "sketch" into the Nano.  If this step seems daunting check out this page on the official Arduino site.
    /*
     **************************************
     ***** www.WhiskeyTangoHotel.Com  *****
     **************************************
     Project Name: Spyrograph Laser (3 axis)

     Start Date:  Feb 2013

     Program Rev History and Notes: 
     Project controls 3 * 24VDC fans (with mirrors attached to the center).  A laser is
     shined onto the mirror.  A '3D' pattern is drawn on the wall with the laser.

     If a control POT is full up, the fans speed is random.
     If a control POT is full down, the fan turns off.
     Else the control POT varies the fan speed manually.

     ***************************************
     */

    // Array starts at VAL 0.  PWM outputs for mirror motors on D9, 10, 11
    int Mirror[] = {
      9, 10, 11};

    // PWM outpts for LED status monitors,  They mimic the fan speed
    int Led[] = {
      3, 5, 6};

    // Analog pins.  Read Pots that control mirror motors
    int Pot[] = {
      1, 2, 3};

    int potvalue[3];  // Store the value of the Pot[] (this value will be 0-1023)
    int mapvalue[3];  // We take the potvalue and rescale it for PWM outputs

    int DelayVal = 2000;      // how fast for the PWM randon speed hold in mSecs
    int KnobBuffer = 10;      // how much of the top end or bottom end of the pot to ignore for random or off fan
    int FullSpin = 0;         // always 0. We want to hit mirrors with full power at program start
    int MaxSpin = 0;          // 0 is full blast. largest val for PWM on mirros during run mode
    int MinSpin = 200;        // 255 is off.  lowest speed/PWM for mirrors

    void setup()
    {
      //Serial.begin(9600);  // Comment in final version, just for debug...

      for (int i = 0; i<=2; i++) { 
        pinMode(Mirror[i], OUTPUT);      // sets the digital pins as output
        pinMode(Mirror[i], OUTPUT);
        pinMode(Mirror[i], OUTPUT);
      }

      for (int i = 0; i<=2; i++) {  
        pinMode(Led[i], OUTPUT);      // sets the digital pins as output
        pinMode(Led[i], OUTPUT);
        pinMode(Led[i], OUTPUT);
      }

      randomSeed(analogRead(0));    // Pin 0 is connected to nothing and will read 'noise' to generate a random seed

      //Spin the fan up full Speed to start and
      //Blink the LEDs as a self test
      for (int i = 0; i<=2; i++) {  
        digitalWrite(Mirror[i],0); // 0 (full low PWM applies full power the the fans)
        digitalWrite(Mirror[i],0);
        digitalWrite(Mirror[i],0);
      }

      for (int i = 0; i <= 50; i++) {  // Blink the LEDs
        digitalWrite(Led[0], 0);       // 0 turns the LED on
        digitalWrite(Led[1], 0);
        digitalWrite(Led[2], 0);
        delay(20);

        digitalWrite(Led[0], 255);     // 1 turns the LED off
        digitalWrite(Led[1], 255);
        digitalWrite(Led[2], 255);
        delay(20);
      } // endSelf Test Loop

    }   //end Setup()

    void loop()
    {
      // Use Array values in a 'for loop' to Read the POTs and control the LEDs and Fans

      for (int i = 0; i<=2; i++) {  
        potvalue[i] = analogRead(Pot[i]);    // read the position of one of the three POTs
        
        //the mapvalue function will rescale the potvalues (0 to 1023) to a range for PWM output (255 to 0)
        mapvalue[i] = map(potvalue[i], 0, 1023, MinSpin, MaxSpin);   // on LOW (0) from the Arduino would turn fan full on due to TC4469 inverted input.

        if (mapvalue[i] <= KnobBuffer) {   // is pot near full up position randomize that mirror speed
          mapvalue[i] = random(MaxSpin, MinSpin);  
          delay(DelayVal);  // if we are random speeding the mirror then delay to allow for the speed adjustment to settle
        }

        if (mapvalue[i] >= MinSpin) {  // if POT is full down then
          mapvalue[i] = 255;           // turn off this fan (255 is off due to TC4469 inverted input.
        }

        analogWrite(Mirror[i],mapvalue[i]);   // Spin the fan to the selected or calulated speed.  0 = full; 255 = off
        analogWrite(Led[i], mapvalue[i]);     // LED brightness mimics fan speed.  0 = bright; 255 = off

        /* Serial.prints below are for debug.  Remove in final version.
        Serial.print(mapvalue[i]);
        Serial.print("   ");
        delay(500);
        */

        } //end control array for loop

        //Serial.println();  //Serial.print for debug.  Remove in final version.

    }  // end void()  end of program code
    -----
    After you clean up all the wiring, the finished goods will tidy up nicely:



    -----
    If all goes well (which it will after you sort through your wiring errors, etc) you will be rewarded with your own laser light show.  Time to get out that Pink Floyd album and enjoy your work. 




    -----
    If your still with me, thanks or checking out the build page.  This is a great project because it has a high visual effect; a 'wow' factor.  Good luck.
    -----
    Link back: Hack A Day
    Link back: Hacked Gadgets
    -----

    Hand of PI (Twitter controlled Robot Hand)

    $
    0
    0
    OBJECTIVE:  Use the Raspberry PI to monitor a Twitter feed and control a mechanical device.

    If you are not interested in the details of the build and just want to see the result you can watch the vid below.  

    -----
    RESULT:  Success!!!  You can control the "The Hand of PI" by sending a tweet to @OurCatDoor.   If  your tweet includes any of the text below, the "Hand of PI" obeys your command.  Valid commands are (lowercase):
    • one (holds up one finger)
    • peace (shows the two finger peace sign)
    • three (three fingers up)
    • hookem (if you are a Texas Longhorn fan this one makes sense)
    • fist (the Hand of PI gets ready to fight)
    • open (ready for a 'high five')
    • finger (well...  this will be the most tweeted command)
    Go ahead, try it!!!  Send a tweet command to @OurCatDoor to let us know you were here.
    -----
    Basically what you are seeing is the Raspberry PI running a Python script searching any tweet sent to @OurCatDoor.  In the video, an iPad sends a tweet to @OurCatDoor that has the command "finger" in it.  It takes a few seconds, but the Raspberry PI finds the tweet, parses it, and find the "finger" command.  The Python script then sets the PI's GPIO ports High/Low.  The PI GPIO is connected to a PICAXE 18M2 (via a HC7404 buffer).  The PICAXE 18M2 reads the PI's GPIO to control five servo motors.  "Hand of PI" reacts with the appropriate gesture.  Watch closely and you can see the text on the screen update as the "finger" command is found and the "Hand of PI" gestures.   There's a lot going on here.  Confused?  This diagram should help (click to see full size):
    Of course this isn't full schematic, but it lays out all the I/O to align with the source code you see below.  Really, the interconnects and 5VDC to the servos, PI, PICAXE, and HC7404 is something anyone wanting to duplicate the project should easily understand given the block diagram and source code.
    -----
    Let's show a few more pics and action videos of the rig before we get into the source code:
     
    -----
    This video is a bit long but demonstrates all the gestures of the "Hand of PI".  The screen in the background shows output from the Python script.  The screen is not needed, but I included it in the video to show the tweets as they are captured.  Note the "Hand of PI" reacts when a new tweet command is found.
    Everyone wants to see the "Hand of PI" flip the bird; that is the last gesture if you want to skip to the end...
    ----
    If you are still with us, enjoy some source code for your reading pleasure.

    First the program that is running on the PICAXE 18M2.  It's job is to read the Raspberry PI's GPIO output and control the five servo motors on the "Hand of PI".

    ' PICAXE 18M2 for RaspPI intergration to Tweeter Controlled Hand Gesture Robot APRIL 2013
    '"THE HAND OF PI"
    ' www.whiskeytangohotel.com
    ' NOTE: PICAXE Program Editor Rev newer than 5.3.6 causes servo jitter***
    ' Other than the minium PICAXE 18M2 'keep alive' 22K R & 10K R
    ' no other R, C, L, etc need for the project.
    ' Everything on PICAXE powered by 4.5VDC
    ' The PICAXE drives the servos straight from the chip.
    ' See pinouts in comments

    ' 0 is Thumb (PICAXE pin 6)
    ' 1 is Pointer (PICAXE pin 7)
    ' 2 is Middle (PICAXE pin 8)
    ' 3 is Ring (PICAXE pin 9)
    ' 4 is Pink (PICAXE pin 10)
    ' Normally Open Button Switch is PICAXE pin 18 (pulled HIGH with 10K)
    ; this button will not be used for the PI intergration

    ' PI GPIO 11 connected to c.0 (PICAXE pin 17)
    ' PI GPIO 13 connected to c.7 (PICAXE pin 16)
    ' PI GPIO 15 connected to c.6 (PICAXE pin 15)

    symbol RaspPI11 = pinc.0
    symbol RaspPI13 = pinc.7
    symbol RaspPI15 = pinc.6

    'Define Servo values to fully EXtend/Open finger
    Symbol Ex_Thumb = 60
    Symbol Ex_Pointer = 60
    Symbol Ex_Middle = 245
    Symbol Ex_Ring = 60
    Symbol Ex_Pink = 60

    'Define Servo values to fully CLose finger
    Symbol CL_Thumb = 225
    Symbol CL_Pointer = 240
    Symbol CL_Middle = 50
    Symbol CL_Ring = 240
    Symbol CL_Pink = 240

    'Init the servos
    servo 0, Ex_Thumb
    servo 1, Ex_Pointer
    servo 2, Ex_Middle
    servo 3, Ex_Ring
    servo 4, Ex_Pink

    pause 400

    'Gesture Subroutines are (2^3 = 8 can be PI Callable)
    ' Valid Tweet commands are: one, peace, three, hookem, fist, finger, wave

    'Insure Open_Hand position at program start
    gosub Open_Hand
    pause 500

    main:  'This loops until hell freezes over

    'Read the RasPI GPIO bus and  jump to gesture sub routine

    If RaspPI15 = 0 and RaspPI13 = 0 and RaspPI11 = 0 then
    gosub Open_Hand
    end if

    If RaspPI15 = 0 and RaspPI13 = 0 and RaspPI11 = 1 then
    gosub One
    end if

    If RaspPI15 = 0 and RaspPI13 = 1 and RaspPI11 = 0 then
    gosub Peace
    end if

    If RaspPI15 = 0 and RaspPI13 = 1 and RaspPI11 = 1 then
    gosub Three
    end if

    If RaspPI15 = 1 and RaspPI13 = 0 and RaspPI11 = 0 then
    gosub Hook_em
    end if

    If RaspPI15 = 1 and RaspPI13 = 0 and RaspPI11 = 1 then
    gosub Fist
    end if

    If RaspPI15 = 1 and RaspPI13 = 1 and RaspPI11 = 0 then
    gosub F_You
    end if

    'If RaspPI15 = 1 and RaspPI13 = 1 and RaspPI11 = 1 then
    'gosub Wave  'wave is pretty hard on the servos, so we commented it
    'end if

    pause 5
    goto main

    ' Gesture Subroutines below:
    Open_Hand:
    servopos 0, Ex_Thumb
    servopos 1, Ex_Pointer
    servopos 2, Ex_Middle
    servopos 3, Ex_Ring
    servopos 4, Ex_Pink
    return ' Open_Hand

    Hook_em:
    servopos 0, CL_Thumb
    servopos 1, Ex_Pointer
    servopos 2, CL_Middle
    servopos 3, CL_Ring
    servopos 4, Ex_Pink
    return 'Hook_em

    F_you:
    servopos 0, CL_Thumb
    servopos 1, CL_Pointer
    servopos 2, Ex_Middle
    servopos 3, CL_Ring
    servopos 4, CL_Pink
    return 'F_you

    One:
    servopos 0, CL_Thumb
    servopos 1, Ex_Pointer
    servopos 2, CL_Middle
    servopos 3, CL_Ring
    servopos 4, CL_Pink
    return 'One

    Peace:
    servopos 0, CL_Thumb
    servopos 1, Ex_Pointer
    servopos 2, Ex_Middle
    servopos 3, CL_Ring
    servopos 4, CL_Pink
    return 'Two

    Three:
    servopos 0, CL_Thumb
    servopos 1, Ex_Pointer
    servopos 2, Ex_Middle
    servopos 3, Ex_Ring
    servopos 4, CL_Pink
    return 'Three

    Four:
    servopos 0, CL_Thumb
    servopos 1, Ex_Pointer
    servopos 2, Ex_Middle
    servopos 3, Ex_Ring
    servopos 4, Ex_Pink
    return 'Four

    Fist:
    servopos 0, CL_Thumb
    servopos 1, CL_Pointer
    servopos 2, CL_Middle
    servopos 3, CL_Ring
    servopos 4, CL_Pink
    return 'Fist

    Wave:  'waves the fingers
    servopos 0, CL_Thumb
    pause 70
    servopos 1, CL_Pointer
    pause 70
    servopos 2, CL_Middle
    pause 70
    servopos 3, CL_Ring
    pause 70
    servopos 4, CL_Pink
    pause 70

    servopos 0, Ex_Thumb
    pause 70
    servopos 1, Ex_Pointer
    pause 70
    servopos 2, Ex_Middle
    pause 70
    servopos 3, Ex_Ring
    pause 70
    servopos 4, Ex_Pink
    return 'Wave
    ----
    Now for the Python script running on the Raspberry PI.  It's job is to search any tweet sent to @OurCatDoor and parse it for a "Hand of PI" command, then set the PI's GPIO for input to the PICAXE 18M2.



    # WhiskeyTangoHotel.com - APRIL 2013   (special thanks to @Rob_Bishop)
    # Error traps entered due to json hitting web site that was down etc.
    # For next added to end of prog to blink LED to show program is running.

    # Import the urllib library to read data from webpages
    import urllib

    # Import the simplejson library to  decode the data read from the webpage
    import simplejson

    # Import the time library for delay and lepse time tracking
    import time
    CurrentTime = time.time()

    # Import the Raspberry Pi GPIO libraries
    import RPi.GPIO as GPIO

    # Set-up the GPIO pins
    # Clear the current set-up
    GPIO.cleanup()

    # Set up the GPIO library to use Raspberry Pi board pin numbers
    GPIO.setmode(GPIO.BOARD)

    # Set pin 11, 13, 15  on the GPIO header to be an output
    GPIO.setup(11,GPIO.OUT)  #PIXACE leg 17 (c.0)
    GPIO.setup(13,GPIO.OUT)  #PIXACE leg 16 (c.7)
    GPIO.setup(15,GPIO.OUT)  #PICAXE leg 15 (c.6)
    GPIO.setup(7,GPIO.OUT)   #Blinkie LED to let us know the prog is running

    # Start with Open Hand
    GPIO.output(11,GPIO.LOW)
    GPIO.output(13,GPIO.LOW)
    GPIO.output(15,GPIO.LOW)
    Last_gesture = "open"
    Error_hit = 0
    print "Hand open.  Waiting for Tweet...","\n"

    # Function to take Twitter handle (e.g. @Raspberry_Pi) as an argument and return the most recent tweet

    # Define the function name and show the arguments
    def Latest_Tweet_to_Twitter_Handle(twitter_handle):
    try:
    # Get the results of a search on Twitter for tweets containing the given hand$
    Twitter_search_results = urllib.urlopen("http://search.twitter.com/search.json?q="+twitter_handle)

    # Decode the data that we got from the webpage to form a list of tweets
    result_list = simplejson.loads(Twitter_search_results.read())

    # The function returns the first result in the list
    return result_list["results"][0]["text"]
    except:
    pass

    # Main body of the program - Get the latest tweet and check if it contains certain words
    # Loop to run forever

    #Twitter commands the hand understands are:
    #one, two, three, hookem, fist, finger, wave

    while(True):
    try:
    #Time since program start in seconds
    DeltaTime = int(time.time() - CurrentTime)

    # Function gets the latest tweet mentioning the handle given in next line
    Tweet=Latest_Tweet_to_Twitter_Handle("@OurCatDoor")

    # START TEST(open): Check if tweet contains the word given in quotation marks
    if "open" in Tweet: # and Last_gesture != "open":
    Last_gesture = "open"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture OPEN HAND","\n"
    # Turn on the LED
    GPIO.output(11,GPIO.LOW)
    GPIO.output(13,GPIO.LOW)
    GPIO.output(15,GPIO.LOW)
    #---END TEST(open)---

    # START TEST(one): Check if tweet contains the word given in quotation marks
    if "one" in Tweet: # and Last_gesture != "one":
    Last_gesture = "one"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture ONE","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.HIGH)
    GPIO.output(13,GPIO.LOW)
    GPIO.output(15,GPIO.LOW)
    #---END TEST(one)---

    # START TEST(peace): Check if tweet contains the word given in quotation marks
    if "peace" in Tweet: # and Last_gesture != "peace":
    Last_gesture = "peace"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture PEACE","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.LOW)
    GPIO.output(13,GPIO.HIGH)
    GPIO.output(15,GPIO.LOW)
    #---END TEST(peace)---

    # START TEST(three): Check if tweet contains the word given in quotation mar$
    if "three" in Tweet: # and Last_gesture != "three":
    Last_gesture = "three"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture THREE","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.HIGH)
    GPIO.output(13,GPIO.HIGH)  
    GPIO.output(15,GPIO.LOW)
    #---END TEST(three)---

    # START TEST(hookem): Check if tweet contains the word given in quotation mar$
    if "hookem" in Tweet: # and Last_gesture != "hookem":
    Last_gesture = "hookem"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture HOOK EM HORNS","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.LOW)
    GPIO.output(13,GPIO.LOW)
    GPIO.output(15,GPIO.HIGH)
    #---END TEST(hookem)---

    # START TEST(fist): Check if tweet contains the word given in quotation mar$
    if "fist" in Tweet: # and Last_gesture != "fist":
    Last_gesture = "fist"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture FIST","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.HIGH)
    GPIO.output(13,GPIO.LOW)
    GPIO.output(15,GPIO.HIGH)
    #---END TEST(fist)---

    # START TEST(finger): Check if tweet contains the word given in quotation mar$
    if "finger" in Tweet: # and Last_gesture != "finger":
    Last_gesture = "finger"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture FINGER F_YOU","\n"
    # TSet the PICAXE inputs
    GPIO.output(11,GPIO.LOW)
    GPIO.output(13,GPIO.HIGH)
    GPIO.output(15,GPIO.HIGH)
    #---END TEST(finger)---

    # START TEST(wavewave): Check if tweet contains the word given in quotation mar$
    if "wavewave" in Tweet: # and Last_gesture != "wave":
    Last_gesture = "wavewave"
    # If it did contain the word then print out the tweet along with a message
    print DeltaTime,"seconds:",Tweet," - Gesture WAVE","\n"
    # Set the PICAXE inputs
    GPIO.output(11,GPIO.HIGH)
    GPIO.output(13,GPIO.HIGH)
    GPIO.output(15,GPIO.HIGH)
    #---END TEST(wavewave)---

    for x in range(0, 10):
    # Wait for xx seconds before repeating
    # Blinkie LED to let us know the program is running
    GPIO.output(7,GPIO.HIGH)
    time.sleep(.1)
    GPIO.output(7,GPIO.LOW)
    time.sleep(1)
    except:
    pass


    -----
    If you are still awake, thanks for checking out the build.  Send a tweet to @OurCatDoor to let us know you were here.
    -----

    Speech Synthesis on the Raspberry PI

    $
    0
    0
    Here is a quick and simple tip to add speech output to jazz up your Raspberry PI projects.  It is easy as installing the "festival" Text to Speech (TTS) application then calling it from the command line or Python script.

    To install "festival" you need to be at a terminal prompt on the Raspberry PI.  Then type:
    $ sudo apt-get install festival festival-freebsoft-utils
    You will get asked to confirm the install.  Type "Y".

    To make the PI talk, just issue a command from the terminal prompt, such as:
    $ echo  "Whiskey Tango Hotel dot com Where stupidity meets reality"| festival --tts
    That's it.  Of course, you can also execute this from a Python script, etc.

    Here's a sample of the output.  Sounds pretty good to me.




    PI in the Oven: Logging Raspberry PI Core Temperatures to Sen.se

    $
    0
    0
    Objective:  Create a method of logging Raspberry PI data to the sen.se website.  In this example I plot the core temperature of two Raspberry PIs, but the method can be adapted to log virtually any form of data that you wish to capture or generate with the PI.  The python code is below to get you running quickly.
    -----

    The graph above is generated by sending data from the PI to the sen.se API.  Sen.se is one cool place.  Their goal is to assist in internet connectivity of personal devices; "the internet of things".  They have widgets, tools, applications, channels and a few other things that I barely understand.  Play around with sen.se some and you will get the idea .  In my application, I have sen.se graphing the core temperature of two Raspberry PIs; a data point every 60 seconds.  Just for fun, I also keep track of the number of reading, calculate an average temperature, and display the temperature change since the last reading.  Sen.se allows you to keep this information private or display it to the public.  If you want to see what my PIs are up to in real time, check out my public SenseBoard.  While you are there you can also check in on the activity of our two house cats as they move through their Tweeting Cat Door.
    ----
    So.... from the graph we see one PI is running about ~12F hotter than the other.  Why?  

    Probably due to a few reasons:  
    RasPI_1 is always running the "motion" webcam software and functioning as an OpenVPN server.  RasPI_1 is also in a fully enclosed case.  (Maybe I should take it out of that case....)

    RasPI_2 is not in a case on is only running my Hand of PI project.  Hand of PI is a robot hand that you can control by sending twitter commands to it.  Click for build page.

    Of course, both PIs are running the temperature logging script.
    -----
    If you are still with me, the python source code is below.  It has been running flawlessly for a while, so it should be solid.  Occasionally Sen.se will go down briefly for maintenance, but that is why I put in the error traps.  Good luck and tweet the Hand of PI to let us know you were here!!!
    ------
    # whiskeytangohotel.com
    # May 2013

    # Python script to read RaspberryPI
    # internal core temp, covert from C to F
    # and log to sen.se for graphing.

    # If you get errros on the import
    # make certain you have the 'apts' installed

    import httplib
    import json as simplejson
    from random import randint
    import time

    # init some vars
    run_number = 0
    tempC = 0
    tempF = 0


    # Enter your private sen.se API KEY in quotes.  Enter the Feed ID# without quotes
    SENSE_API_KEY = "x1xxxxxy2yyyyyyz3zzzz"  
    FEED_ID1 = 12345

    # Function to format for sen.se
    # The try/expect are there to trap errors if sen.se goes down
    # or is slow.  This keeps the script running.
    def send_to_opensense(data):
    try:
    # prepare data 
    datalist = [{"feed_id" : FEED_ID1, "value" :data['F']},]
    headers = {"sense_key": SENSE_API_KEY,"content-type": "application/json"}
    conn = httplib.HTTPConnection("api.sen.se")
    # format a POST request with JSON content
    conn.request("POST", "/events/", simplejson.dumps(datalist), headers)
    response = conn.getresponse()
    conn.close()
    except:
    pass   

    while(True):

    # The try/expect are there to trap errors if sen.se goes down
    # or is slow.  This keeps the script running
    try:
    # read the PI core temperture and store in tempC
    # then convert from C to F and send the data to sen,se
    tempC = int(open('/sys/class/thermal/thermal_zone0/temp').read()) / 1e3
    tempF = (tempC * 1.8) + 32
    run_number = run_number + 1
    print "Run:", run_number, "    tempC:", tempC, "    tempF:",tempF
    data = { 'F' : tempF}
    send_to_opensense(data)
    time.sleep(60)
    except:
    pass
    -----




    VistaQuest VQ1005 Controlled by TI MSP430 LaunchPad

    $
    0
    0
    We show you how.


    There are a lot of things about the TI MSP430 that I like. First of all, the price from TI is crazy cheap at $4.30!!! Hats off to TI for making their development kit so inexpensive that anyone who even wants to casually experiment with micro-controllers can do so. Even though it only cost $4.30, the MSP430 LaunchPad is a very capable micro-controller and the free SDK (Code Composer Studio) used to program the MSP430 is nice as well.

    Long ago I broke open a VistaQuest VQ1005 and used a 555 timer to snap pics automatically. I wanted to replace the 555 timer with the TI MSP430 LaunchPad because 1) it would allow more programmable control over the delay between pics and 2) the additional I/O on the MSP430 would allow me to get around the VistaQuest VQ1005 wanting to go to sleep after 60 seconds on inactivity.

    The integration of the VistaQuest VQ1005 to the MSP430 LaunchPad is one that is cheap and easy given some guidance. If you are looking for a uC project to get your feet wet, this hack is sure to work and will cost less than twenty bucks if you watch for deals on the VistaQuest VQ1005. By now almost every household has an old digital camera around, so it's easy to substitute an old 'free' camera for the VistaQuest VQ1005 if you want. The integration concept to the MSP430 LaunchPad is the same.

    First, get a TI MSP430 LaunchPad from here and a VisaQuest VQ1005.

    They will look like this:
    ----

    If you don't know which is the MSP430 LaunchPad and which is the camera then you can stop reading now...

    The first thing I did was solder header pins to the Vcc, GND, P1.5, and P1.7 "holes" on the MSP430 LauchPad. We are going to be connecting wires from the camera to those places. If you want, you can just solder the wires from the camera straight into the holes, but the header pins will allow you to unplug the camera from the MSP430 LaunchPad if you ever so desire.

    Next open up the VistaQuest VQ1005. This is easy. You will remove four small screws on the outside case. Once inside you will see two PCB boards. The trigger board (the board that presses the button to take the pic) looks like this:


    See the red circle labeled "T"? Solder a thin wire to that location. This is the trigger wire that will ultimately be connected to P1.5 on the MSP430 LaunchPad. We are going to use the MSP430 to bring this wire LOW (zero volts) to take a pic. Don't worry, the code to do this and more pics are below to help you with this.

    Now we want to remove the VistaQuest VQ1005 "ON/OFF" control PCB. This is the same PCB that holds the camera lens and is attached with two small screws. Remove the screws and remove the PCB. See the red circle labeled "O" in the pic below? This is the "ON/OFF" control point. Solder a wire to that point. This wire will ultimately connect to P1.7 on the MSP430 LaunchPad. We are going to use the MSP430 to bring this wire LOW (zero volts) to make sure the camera is ON and ready to take a pic. Again; don't worry, the code to do this and more pics are below to help you with this.


    Put the VistaQuest VQ1005 camera back together. It's as simple to put together as it was to take apart. Be careful not to pull off the wires you just soldered to the two PCBs. Depending on how you route your wires you may have to make a notch or two in the camera case.

    We are almost done. See the pics below to guide you through how to connect up the VistaQuest VQ1005 to the MSP430. As you can see, I powered the whole operation with two AA batteries (3VDC). The one nice thing about the LaunchPad is that it has a USB port input that you could use to provide the power for the project. Basically, you would remove the two AAs and use your USB port to power the rig.

    The pic below shows the (T)rigger wire from the VistaQuest VQ1005 attached to the MP430 LaunchPad P1.5 output pin. Also, the +3VDC power from the two AAs connected to the Vcc header pin we soldered to the MSP430 LaunchPad.
    ---
    Below is a shot of the (O)n/(O)ff control wire from the VistaQuest VQ1005 attached to the MSP430 LaunchPad P1.7 output pin. You can also see the black wire that goes to the NEG terminal of the 3VDC AAs. Note that at the bottom right I have used the Vcc and GND header pins that were already on the MSP430 LaunchPad to power the VistaQuest VQ1005 camera. These wires simple go to the "+" and "-" place on the VistaQuest VQ1005 where the battery would connect. BTW, I know the VistaQuest VQ1005 uses one 1.5VDC battery to power it and we are pushing 3VDC into it. Don't worry about that, the VistaQuest VQ1005 has the smarts to handle this.

    ---
    Below is a pic of the two AAs that provide the 3VDC power. The battery pack is held onto the MSP430 LanchPad with Velcro.

    ---
    In the end it should all look something like this:
    ---
    We tested on location where our cats relax in the sun. We used the 1000+ images that the rig took and made this short video:

    ---
    Let us know if you give this a try and what your results are. Good luck, have fun. Please reference www.WhiskeyTangoHotel.Com if you found this useful.

    Oh, one more thing... The code below makes it all happen.

    The code flashes the red LaunchPad LED during camera power up.
    The code flashes the green LaunchPad LED when a pic is taken.
    The code alternates flashing the green and red LED as it waits to take the next pic. The LEDs alternate more rapidly as the VistaQuest VQ1005 gets closer to taking the next pic.

    Modify it if you want.

    ////////begin MSP430 Code//////////

    // This program interfaces the MSP430 Laucnpad uC dev board
    // to a VistaQuest VQ1005 Digital Keychain Camera.
    // This allows to camera to take picutes at pre-defined intervals
    // automatically without any manual interaction.

    // Cost: MSP430 Launchpad: $4.30 from Texas Instruments
    // VistaQuest VQ1005: $5-10 from Walmart, eBay, etc

    // Developed at: IronJungle.Com / August 2011 / Open Source, use and modify

    #include msp430g2231.h

    void main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
    P1DIR |= BIT0; // Set Red LED (P1.0) to output direction
    P1DIR |= BIT6; // Set Green LED (P1.6) to output direction
    P1DIR |= BIT5; // Set Camera Trigger (P1.5) to output direction (Low is Take pic)
    P1DIR |= BIT7; // Set Camera ON/OFF control (P1.7) to output direction (active LOW)

    volatile long i; // General loop var
    volatile long j; // General loop var

    volatile long d; // d is delay between pics
    d = 15000; // set the value of d per guidelines below
    //d = 10000; about 15 secs between pics
    //d = 15000; about 35 secs between pics
    //d = 20000; about 60 secs between pics
    //d = 40000; abput 4.5 mins between pic
    //increase d for longer delay between pics

    P1OUT |= BIT5; //need to start with tigger high (because LOW triggers camera

    for (;;) // Start of Endless Loop
    {

    P1OUT &= ~(BIT0 + BIT6); // both LEDs off

    P1OUT &= ~BIT7; // Force low to turn on camera
    for (i=0; i<5 data-blogger-escaped-br="br" data-blogger-escaped-i="i"> { // Delay some while camera 'boots'
    P1OUT ^= BIT0; // Flash/Toogle Red LED while 'booting'
    for (j=0; j<2000 data-blogger-escaped-br="br" data-blogger-escaped-j="j"> } //

    P1OUT |= BIT7; // Return camera control to High
    for (i=0; i<5 data-blogger-escaped-br="br" data-blogger-escaped-i="i"> { // Delay some just 'because'....
    P1OUT ^= BIT0; // Flash/Toogle Red LED while delaying
    for (j=0; j<2000 data-blogger-escaped-br="br" data-blogger-escaped-j="j"> } //


    P1OUT &= ~(BIT0 + BIT6); // both LEDs off

    // Blink the Green LED to show a pic is being taken
    // and allow some delay for disk write, etc.
    P1OUT &= ~BIT5; // Drive Trigger signal low to take pic
    for (i=0; i<100 data-blogger-escaped-br="br" data-blogger-escaped-card="card" data-blogger-escaped-delay="delay" data-blogger-escaped-etc.="etc." data-blogger-escaped-i="i" data-blogger-escaped-pic="pic" data-blogger-escaped-process="process" data-blogger-escaped-save="save" data-blogger-escaped-sd="sd" data-blogger-escaped-some="some" data-blogger-escaped-to="to"> {
    P1OUT ^= BIT6; // Blink the Green LED
    for (j=0; j<2000 data-blogger-escaped-br="br" data-blogger-escaped-j="j"> } // end Green Blink/delay for pic
    P1OUT |= BIT5; // return tigger to high (because LOW triggers camera)

    P1OUT &= ~BIT0; // Turn off Red LED and
    P1OUT |= BIT6; // Green LED ON to set up for alternating Red/Green flash rountine below
    // The loop below alternates the LaunchPad Green/Red LEDs; speeding up until pic is taken
    // Time between pics is define by the value of "d" set above

    for (j=1; j{
    P1OUT ^= BIT0 + BIT6; // Toggle LEDs
    for (i = 1; i<(d-j); i++); // A decreasing delay to make LEDs alternate faster until trigger
    } //end loop that alternates the LaunchPad Green/Red LEDs


    } // end of endless loop
    } //main

    -----

    Raspberry PI: Charting Ambient vs Outside Temperature

    $
    0
    0
    How to use a Raspberry PI to chart ambient temperature vs outside temperature.  Source code and schematics below.

    What you need:
    -----
    What you get:

    Reading the graph above is pretty obvious.  It plots the temperature of the DS18B20 sensor connected to the Raspberry PI vs. the outside temperature that is provided by a local weather forecast feed.  Just for fun, we also display Min and Max temperatures (which can be reset).
    -----
    The graphing is provided by sen.se.  The sen.se site offers a lot of flexibility with "the internet of things".  sen.se is free.  Sign up and scan the tutorials.  The site is well laid out and the tutorials are very straight forward; you'll be an expect in no time.  Basically, you want to create a "channel" for your Raspberry PI by 'adding a device'.  sen.se will give you a 5 digit channel number for your RasPI and a very long passphrase that will be your personal identifier.  You will need both of these for the source code below.
    -----
    Next, let's connect the DS18B20 to the Raspberry PI.  The DS18B20 transmits its temperature reading via I2C bus.  Just follow the tutorial at Adafruit.  The connection is simple and looks like this:
    -----
    Load the Python script below into your Raspberry Pi and run it.  Be certain you enter your personal passphrase identifier and the device channel code that you got earlier from sen.se.  After you run the Python script head back over to sen.se.  You should see that sen.se has detected a 'heartbeat' from your Raspberry PI.  After that, it is just a matter of configuring one of the graphing apps on sen.se.  You can make your sen.se data public or private and there are many many tools to manipulate and display your data.
    -----
    Good luck!  Python script for the RasPI follows:

    # WhiskeyTangoHotel.Com
    # June 2013
    # Program reads DS18B20 temp sensor and plots value to sen.se
    # DS18B20 connections via AdaFruit tutorial
    # With thanks to @Rob_Bishop

    # This program is feed customized for RasPI(2)

    import httplib
    import json as simplejson
    from random import randint
    import time
    import os
    import glob

    # Pass os commands to set up I2C bus 
    os.system('modprobe w1-gpio')  
    os.system('modprobe w1-therm')

    base_dir = '/sys/bus/w1/devices/'
    device_folder = glob.glob(base_dir + '28*')[0]
    device_file = device_folder + '/w1_slave'

    run_number = 0

    SENSE_API_KEY = "long sen.se passphase here. note that it is in quotes"
    FEED_ID1 = 12345  # five digit sen.se channel code.  note it is NOT in quotes

    def read_temp_raw():  #read the DS18B20 function
        f = open(device_file, 'r')
        lines = f.readlines()
        f.close()
        return lines

    def read_temp(): #process the raw temp file output and convert to F
        lines = read_temp_raw()
        while lines[0].strip()[-3:] != 'YES':
            time.sleep(1)
            lines = read_temp_raw()
        equals_pos = lines[1].find('t=')
        if equals_pos != -1:
            temp_string = lines[1][equals_pos+2:]
            ambC = float(temp_string) / 1000.0
            ambF = ambC * 9.0 / 5.0 + 32.0
            return ambF

    def send_to_opensense(data):
    #    print  >> fout, "\t=> Sending to OpenSense: %s" % data
    try:
    # prepare data 
    datalist = [{"feed_id" : FEED_ID1, "value" :data['F']},]
    headers = {"sense_key": SENSE_API_KEY,"content-type": "application/json"}
    conn = httplib.HTTPConnection("api.sen.se")
    # format a POST request with JSON content
    conn.request("POST", "/events/", simplejson.dumps(datalist), headers)
    response = conn.getresponse()
    # you may get interesting information here in case it fails
    #   print >> fout, response.status, response.reason
    #   print >> fout, response.read()
    conn.close()
    except:
    pass

    while(True):
    try:
    run_number = run_number + 1
    ambF = read_temp()
    print "RasPI(2) Ambient Run:", run_number, "    ambF:",ambF
    data = { 'F' : ambF}
    send_to_opensense(data)
    time.sleep(300)
    except:
    pass
    -----








    Failure Tutorial: All hail the Mighty Bypass Cap

    $
    0
    0
    It's frustrating.  You design a great project, get it working perfectly on the breadboard with that nice beefy bench power supply only to discover it stops working in "the real world".  What the hell's going on?
    -----

    Here is a short video showing how everything just stops when the bypass cap from my Etch-a-Sketch to chart Temperature vs Time is removed.  Connect the bypass cap and, like magic, everything is back to normal.  Pretty amazing, huh?
    -----
    If you follow DIY projects on the web you see this issue frequently.  The fix often is simply to add a capacitor between power and ground.  This is called a by-pass cap or decoupling cap.  They are most useful in projects with electrical noise or where larger loads like motors, solenoids, relays, etc. are involved.

    As I said, this recently happened to me.  I had the great idea to use two stepper motors mounted to an Etch-a-Sketch to chart Temperature vs Time.  The project came off fine in the end, but not without a bump in the road.

    The rig was stable with one stepper motor.  However, adding the second stepper motor made everything 'wacky'.   Sometimes the PICAXE 18M2 microcontroller would not even accept new code downloads.  WTH?
    -----
    Below is short video of the finished rig in action. (Thanks Hack-a-Day for featuring it!)  Take a look at the build page if you are interested in duplicating the project.

    -----
    Thanks for the visit and "All hail the mighty bypass cap"!!!

    Exploding a Capacitor

    $
    0
    0
    Every electronics DIY/hobby site seems to have a cap exploding.  This is the WhiskeyTangoHotel oblatory example.  Actually, this is the first capacitor we have ever exploded (on purpose that is).  The cap is spec'd at 15VDC.  We reversed biased it with 40VDC.




    Arduino Compass Bearing Following Robot

    $
    0
    0
    Objective:
    Build a robot that automatically tracks a user provided compass heading.  Create a proof of concept indoor model and then scale up for outdoor use.


    -----
    If you are not interested in the build details and just want to see the result take a look at the video below.  Basically, we are using an Arduino Nano and a LM303DLHC Micro Electro Mechanical (MEMs) compass sensor to read and track a compass bearing.

    As shown in the video, the robot continuously reads it's current compass bearing and adjusts to track the desired user direction.  It does this by turning the wheel motors on or off.  To keep things simple, the two motors are powered at full speed (no PWM).  The two motors are never on at the same time.
    -----
    Components:
    Modern technology keeps the BOM small and puts the magic in the software that is shown later.















    Ardunio Nano microcontroller for the brains.  About $10USD if you shop around.
    -----












    LM303DLHC MEMs sensor provides the magnetic compass reading input for the Arduino.  The LM303DLHC is has tilt compensation and accelerometers on board that can be used to detect hitting obstacles.  About $7USD shipped from "eBay China".
    -----













    754410NE H-Bridge to drive the motors.  (The Ardunio can't source enough current to drive the motors.)  At is stands now the project only powers the drive wheels to move forward, however; we selected the 754410NE because with just a few wires and lines of code it can drive both motors forward and reverse. About $3USD.
    -----
    Get the above parts, a breadboard, two DC motors, wheels, a power source, etc. and hook it all up.  In the diagram below I tried to closely mimic the breadboard setup in the pics:

















    -----
    Scaling Up:
    We have a few APEX PA74 op amps that are capable of driving VERY high current loads.  We are in the process of scaling the build for outdoor use with strong 12VDC motors.  Here is a quick demo vid of the platform in action:


    -----
    If you are still with us, here is the Arduino Nano code.  This is the most simple example.  Other options include using the accelerometers to detect obstacles and drawing patterns (triangle, square, out and back, etc.)

    /*
     **************************************
     ***** www.WhiskeyTangoHotel.Com  *****
     **************************************
        Project Name: LM303DLHC Compass Robot
     
        Start Date:  Nov 2013
     
        Program Rev History and Notes:
          Special thanks to pololu.com!  See their GitHub site.
       
          Point robot in desired direction then press reset button.
          Put robot on ground.  After 5 sec delay the robot will track the desired bearing.
       
          This version moves the robot on the user desired bearing forever.
          Other versions detect objects (bump) and draw patters (out and return, box, triangle)

     ***************************************
     */

    // Define Variable Types and include.h
    #include <Wire.h>
    #include <LSM303.h>

    int LeftWheel = 11;         //left motor on output D11
    int RightWheel = 10;       //right motor on output D10

    //**************************************

    LSM303 compass;

    int gotoheading;  //global var for the initial robot direction.  Or Press reset button...

    void setup() {
       pinMode(LeftWheel, OUTPUT);      // sets the digital pin as output
       pinMode(RightWheel, OUTPUT);      // sets the digital pin as output
     
       digitalWrite(LeftWheel, LOW);
       digitalWrite(RightWheel, LOW);
     
       Serial.begin(9600);  // Used for debug only
     
       Wire.begin();
       compass.init();
       compass.enableDefault();

      //read the direction that you want the robot to go.  LM303 Header pins point to direction
      compass.read();
      gotoheading = compass.heading((LSM303::vector){0,-1,0});

      delay(5000);  // dealy xx mSecs after power up before doing anything

      // Calibration values. Use the pololu.com Calibrate example program to get the values for your compass.
      compass.m_min.x = -872; compass.m_min.y = -757; compass.m_min.z = -212;
      compass.m_max.x = +461; compass.m_max.y = +511; compass.m_max.z = 844;

    }

    void loop() {
      compass.read();
      int heading = compass.heading((LSM303::vector){0,-1,0});

     ///*  Print to the PC monitor.  Debug only.
      Serial.print(gotoheading);
      Serial.print(" Current is: ");
      Serial.print(heading);

    //*/  //End Serial print to PC comment block
     
      if (heading > gotoheading)    //turn the bot left by turning on right motor
        {
          digitalWrite(LeftWheel, LOW);
          digitalWrite(RightWheel, HIGH);
          Serial.println("  OFF  --  ON");
        }
       
      if (heading <= gotoheading)  //turn the bot right by turning on left motor
        {
        digitalWrite(LeftWheel, HIGH);
        digitalWrite(RightWheel, LOW);
        Serial.println("  ON --  OFF");
        }

      delay(0);   //in mSecs

    }  //void loop
     
    -----

    DS Nano v2 Oscilloscope Performance

    $
    0
    0
    The DS Nano v2 is a very low cost 1-channel oscilloscope that, for the ~$80US price, has served me pretty well for my hobby electronics.  The scope came with a nice soft case and two sets of "probes".  It has other features as well.

     ----
    This page is not intended to put down the little DS Nano v2.  The scope behaves as advertised.  For example, I had a project that required a piezo sensor input.  Things were not working according to plan and the DS Nano v2 was able to capture the low frequency signal from the piezo and suggest that a diode was needed to tame the signal for use as a microcontroller input.


    Here is another example of the DS Nano v2 doing a great job showing variable pulse width from a simple PICAXE18 program:

    ----
    That said, the DS Nano v2 does have its limits and I always wanted to compare it to a "real" oscilloscope.  I was able to get my hands on a Tektronix MSO2042 Oscilloscope and Tektronix AFG3252 Signal Generator.  The AFG3252 can produce waveforms up to 240MHz extremely accurately.  Well in the range for our purposes as an input source.
    -----
    The table below compares some key specs of the DS Nano v2 and the MSO2042:
    Again, for our test purposes the Tektronix MSO2042 Oscilloscope is way more than adequate to test the DS Nano v2. 
    -----
    So what about the results?  Below are some screen shots of signals with increasing frequency.  The DS Nano v2 is the smaller screen in the foreground.  The Tektronix MSO2024 is the larger screen in the background.  Both  oscilloscopes are connected to the same signal source in parallel with no concern for impedance matching (note the ringing).   You can immediately see that DS Nano fails miserable at detecting the  high frequency ringing.  But let's be fair; the DS Nano specs is 200KHz for analog bandwidth, so this result is not a surprise.

    One important thing to look at in the pics below are the voltage level cursor lines on the DS Nano v2.  You will notice that there is really no attenuation in the signal the DS Nano v2 displays as we test to its 200KHz bandwidth; that's good.  However, the limit of the 1MS/s sample rate does start to show.  The 100KHz signal pic really shows how the DS Nano v2 sample rate effects the displayed output.

    All that said, it you are looking for a <<$100US scope that will fit in your pocket, the DS Nano v2 is a handy choice.  If you use it within it's specs it is a great value!  If you need more call Tektronix!
    ----


    ----







    ----

    Graphing Twitter Mentions with the Raspberry PI

    $
    0
    0
    Objective:
    Use the Raspberry PI to monitor Twitter for specific words or hashtags that are tweeted.  Graphically display the results on a publicly viewable webpage.
    -----
    If you are not interested in the build details and just want to see the result then take a look at this short video.  We had the RasPI set up to monitor for a "Tektronix" or "Agilent" mention in any and every message Twitter receives.

    The video shows an iPad tweeting the message "Tektronix and Agilent. RasPI HashVote Test".  Since this tweet contains both search words the RasPI is monitoring for, each counter gets incremented and the graph is updated.

    Tektronix and Agilent are well know test and measurement companies.  In general, they both get tweeted about the same number of times each day.  However, take a look at Agilent's graph the day they released their (not so good) quarterly earnings.  The graph clearly shows the extra Twitter chatter on Agilent due their earnings announcement. 
    -----
    So how does it work?
    You're going to need a Raspberry PI connected to the internet (duh?), a Twitter account, and an open.se account.

    Then you are going to create a Python script that runs on the Raspberry PI to search for Tweets and update the counters and graph on your open.se account.  The Python code is listed below.  If you are going to track two terms (as in the example above) you will have a separate Python script running for each search term.  You are going to need API authorization/access tokens for your Python scripts.  Don't panic; that's easy.

    - Twitter API Token: Go to https://dev.twitter.com/.  You will need to create API tokens for each Python script you have running.  In the example above I am tracking two words; "Tektronix" and "Agilent", so I need to set up two API tokens.  If you decide to change the search terms from "Tektronix" or "Agilent" to "Happy" and "Sad" you will not have to create new API tokens.  Just simple change the search terms in the RasPI Python scripts.

    - open.se API Token: Go to http://open.sen.se/ and create a "Channel" for each search term you want to track.  Again, the example above tracks two terms being tweeted so two channels are created.  After you create a "Channel" you will get a "FEED ID" for each channel.  You will also get an API token that is assigned to your account.  This API token is private to you and the same for each channel.  Then, play around with the "apps" at open.se to created graphs, counters, gauges, and a ton of other cool things.  Their tutorials are good, so I wont explain how to do that here.

    Now boot your Raspberry PI, open your favorite text editor, and copy/past in the code below.  Replace the 'xxxxxxxx' with your custom API token and FEED ID information.  Again, you will need a Python script running for each Twitter term you are searching for.  There is probably a way to do this with one Python script, but I'm not that smart.

    Run the script and watch the data flow and the counters update.  During debug and test, I would suggest tracking a commonly tweeted word such as "retweet" or "ipad".  That will help with the debug.  If you track a term like "WhiskeyTangoHotel.com" you may be waiting a while to see a result.  ;)

    Mostly, the system runs perfectly, but for reasons unknown to me the Python script will 'freeze' from time to time.  I have had them running for days and days at a time without issue, but from time to time the freeze just happens.  If you know why, please enter into comments section of the youtube demo shown above.
    -----
    #  RasPI Python script to search Twitter for a string and post to open.se
    #  by WhiskeyTangoHotel.Com with special thanks to Sparkfun and twython
    #  FEB 2014



    import time
    from twython import TwythonStreamer
    import httplib
    import json as simplejson

    #
    # Search term that you want to find and count
    #
    Search_Term = 'WhiskeyTangoHotel.Com' # Not case sensitive. TweET = tweet.
    global Search_Term_Counter 
    Search_Term_Counter = 0   # counts the finds

    localtime = time.asctime( time.localtime(time.time()) )
    print localtime
    print "START searching for: " + Search_Term

    # Twitter application authentication
    APP_KEY = 'xxxxxxxxxxxxxxxx'
    APP_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    OAUTH_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    OAUTH_TOKEN_SECRET = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

    # open.se application authentication
    SENSE_API_KEY = "xxxxxxxxxxxxxxxxxxxxx"
    FEED_ID1 = 12345  #FeedID for Hashvote_Counter2 on open.se

    # def function to send Search_Term_Counter to open.se for processing
    def send_to_opensense(data):
    #    print  >> fout, "\t=> Sending to OpenSense: %s" % data
            try:    # error trap to continue run if crash during open.se postings
                    # prepare data     
                    datalist = [{"feed_id" : FEED_ID1, "value" :data['F']},]  #:data string not important for counting, but leave it
                    headers = {"sense_key": SENSE_API_KEY,"content-type": "application/json"}
                    conn = httplib.HTTPConnection("api.sen.se")
                    # format a POST request with JSON content
                    conn.request("POST", "/events/", simplejson.dumps(datalist), headers)
                    response = conn.getresponse()
                    # you may get interesting information here in case it fails
                    #   print >> fout, response.status, response.reason
                    #   print >> fout, response.read()
                    conn.close()
            except:
                    pass

    # def Hash_Counter called from 'class' below.  Add custom code here.
    def Hash_Counter ():  
            global Search_Term_Counter
            Search_Term_Counter = Search_Term_Counter + 1
            localtime = time.asctime( time.localtime(time.time()) )
            print localtime
            print "Search Term " + Search_Term + " found " + str(Search_Term_Counter) + " times."
            data = {'F' : Search_Term_Counter}
            send_to_opensense(data)
            
    # Setup callbacks from Twython Streamer
    try:  # error trap to continue run if crash due to offsite TwythonStreamer
    class Search_Twitter (TwythonStreamer):
    def on_success(self, data):
    #Hash_Counter()         # for debug only
    if 'text' in data:
    Hash_Counter()  # found so call the Hash_Counter def
    print data['text'].encode('utf-8')  # Typically REM'd unless for debug
    print "-----" # seperator to format the screen.
    time.sleep(10)  #pause xx seconds just to keep from flooding sen.se with data
    except:
    pass
       
    # Create streamer to search Twitter fot the Search_Term var
    try:
            stream = Search_Twitter(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
            stream.statuses.filter(track=Search_Term)
    except:  #  KeyboardInterrupt:  # helps during debug to exit more gracefully on CNTRL C
            pass  
    -----
    Good luck and thanks for the visit!


    RasPI + RGB LED = Color of Twitter

    $
    0
    0
    This project uses a Raspberry PI to scan all posted Tweets in real time for the mention of a color.  When a color is Tweeted  the Red, Green, and/or Blue segments of a RGB LED are turned on to display the Tweeted color.  Video demo below:

    -----
    The project is pretty cool and simple to duplicate.  You should be able to just copy/past my Python script below into your favorite RasPI editor and go from there.

    Note that the code expects the RGB LED to be connected to I/O Pins 11 (Red), 15 (Green) and 13 (Blue).  Also, be sure to add a current limiting resistor to each of the three I/O pins; not the RGB LED ground pin.  My RGB LED was spec'd for 330 Ohm resistors.  This picture should help identify Pins 11, 15, and 13:
    -----
    You will also need to establish your own Twitter API Token.  Don't worry; it's easy if you already have a Twitter account.  To get them go to https://dev.twitter.com/.  Enter these API Token values where the X's are in the source code below.
    -----
    #  Program to search Twitter to control a RGB LED
    #  by WhiskeyTangoHotel.Com with special thanks to Sparkfun and twython
    #  Tracks a tally count after each find
    #  APRIL 2014

    import time
    import datetime # to allow timestamp math
    import RPi.GPIO as GPIO
    from twython import TwythonStreamer

    #  PI I/O 11 = Red
    #  PI I/O 13 = Blue
    #  PI I/O 15 = Green
    #  Red + Blue = Orange
    #  Red + Green = Pink
    #  None on = Black

    # GPIO pin number of LED
    Red = 11
    Blue = 13
    Green = 15

    # Setup GPIO as output
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(Red, GPIO.OUT)
    GPIO.output(Red, GPIO.LOW)

    GPIO.setup(Green, GPIO.OUT)
    GPIO.output(Green, GPIO.LOW)

    GPIO.setup(Blue, GPIO.OUT)
    GPIO.output(Blue, GPIO.LOW)

    # Twitter application authentication

    APP_KEY = 'xxxxxxxxxxxxxxxxxxxx'
    APP_SECRET = 'xxxxxxxxxxxxxxxxxxxx'
    OAUTH_TOKEN = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
    OAUTH_TOKEN_SECRET = 'xxxxxxxxxxxxxxxxxxxx'

    # Search terms placed in array TERM[]   # will find any term within the array ['one', 'two', 'three']   CASE SENSITIVE
    TERM = []
    TERM.append('red')#TERM[0]
    TERM.append('blue')#TERM[1]
    TERM.append('green')#TERM[2]
    TERM.append('orange')#TERM[3]
    TERM.append('pink')#TERM[4]

    LED_secs_on = 30  # when found; how long to burn the LED

    localtime = time.asctime( time.localtime(time.time()) )
    print localtime
    t0 = datetime.datetime.now()  # for timestamp math
    print 'Self testing RGB LED...'
    #Test RGB LED
    i = 0
    for i in range(0,5):
    print 'RED...  GREEN...  BLUE...'
    GPIO.output(Red, GPIO.HIGH)
    time.sleep(0.3)
    GPIO.output(Red, GPIO.LOW)

    GPIO.output(Green, GPIO.HIGH)
    time.sleep(0.3)
    GPIO.output(Green, GPIO.LOW)

    GPIO.output(Blue, GPIO.HIGH)
    time.sleep(0.3)
    GPIO.output(Blue, GPIO.LOW)
    i = i + 1

    print ''
    print "START searching for TERMS: "
    print TERM[0]
    print TERM[1]
    print TERM[2]
    print TERM[3]
    print TERM[4]
    print '................................'
    print ''

    Tally_0 = 0
    Tally_1 = 0
    Tally_2 = 0
    Tally_3 = 0
    Tally_4 = 0

    # Setup callbacks from Twython Streamer
    class BlinkyLED(TwythonStreamer):
            def on_success(self, data):
    global Tally_0
    global Tally_1
    global Tally_2
    global Tally_3
    global Tally_4

    if 'text' in data:
    check_string = data['text'].encode('utf-8')

    if TERM[0] in check_string:
    print TERM[0] + ' found on ' + time.asctime( time.localtime(time.time()) )
    print ''
    Tally_0 = Tally_0 + 1
    print data['text'].encode('utf-8')
    GPIO.output(Red, GPIO.HIGH)


    if TERM[1] in check_string:
    print TERM[1] + ' found on ' + time.asctime( time.localtime(time.time()) )
    print ''
    Tally_1 = Tally_1 +1
    print data['text'].encode('utf-8')
    GPIO.output(Blue, GPIO.HIGH)

    if TERM[2] in check_string:
    print TERM[2] + ' found on ' + time.asctime( time.localtime(time.time()) )
    print ''
    Tally_2 = Tally_2 +1
    print data['text'].encode('utf-8')
    GPIO.output(Green, GPIO.HIGH)

    if TERM[3] in check_string:
    print TERM[3] + ' found on ' + time.asctime( time.localtime(time.time()) )
    print ''
    Tally_3 = Tally_3 + 1
    print data['text'].encode('utf-8')
    GPIO.output(Red, GPIO.HIGH)
    GPIO.output(Green, GPIO.HIGH)

    if TERM[4] in check_string:
    print TERM[4] + ' found on ' + time.asctime( time.localtime(time.time()) )
    print ''
    Tally_4 = Tally_4 + 1
    print data['text'].encode('utf-8')
    GPIO.output(Red, GPIO.HIGH)
    GPIO.output(Blue, GPIO.HIGH)

    if TERM[0] in check_string or TERM[1] in check_string or TERM[2] in check_string or TERM[3] in check_string or TERM[4] in check_string:
    print ''
    print 'SCORE:'
    print TERM[0] + ' = ' + str(Tally_0)
    print TERM[1] + ' = ' + str(Tally_1)
    print TERM[2] + ' = ' + str(Tally_2)
    print TERM[3] + ' = ' + str(Tally_3)
    print TERM[4] + ' = ' + str(Tally_4)
    print ''
    print str(Tally_0 + Tally_1 + Tally_2 + Tally_3 + Tally_4) + ' total finds after ' + str(datetime.datetime.now() - t0)
    print '--------------------------------'
    print ''
    time.sleep(LED_secs_on)    # keep LED on for xx secs
    GPIO.output(Red, GPIO.LOW)  # turn off the LED
    GPIO.output(Green, GPIO.LOW)
    GPIO.output(Blue, GPIO.LOW)

    # Create streamer
    try:
            stream = BlinkyLED(APP_KEY, APP_SECRET, OAUTH_TOKEN, OAUTH_TOKEN_SECRET)
            stream.statuses.filter(track=TERM)
    except KeyboardInterrupt:
            GPIO.cleanup()

    -----
    That's it.  Hope you give it a try!

         

    Tektronix MDO3000 Motorcycle Gear Position Indicator

    $
    0
    0
    -----
    This project is a variant of my ultra-low cost DIY Suzuki DL1000 DIY Gear Position Indicator (GPI) with a few changes:
    • For a microcontroller the Ardunio Nano is used to digitize the signal from the bike's ECM instead of a PICAXE 18M2.
    • The gear position output is displayed on a Tektronix MDO3000 oscilloscope instead of a two dollar seven segment LED.  The MDO3000 is an amazing instrument from Tektronix; read about it here.
    This is probably the only oscilloscope based motorcycle GPI on the planet.  It is also perhaps the most impractical and expensive way to build a motorcycle GPI but that was really the entire point of the project. ;)
    -----
    For those that are not interested in the project details and just want to see the result take a look at this 60 second video:
    -----
    Basically, here is what is going on....  Analog input A1 on the Arduino microcontroller is configured as a ADC pin and monitors an output on the bike's ECM.  See the yellow wire in the video?  That is patched into a signal on the ECM that outputs a value of 0-5VDC depending on the gear that the bike is in.  

    We digitize that ECM voltage on the yellow wire and depending on the voltage of that signal (what gear the bike is in) branch to code that Pulse Width Modulates (PWM) two Arduino digital output (digital output Pin 6 and 5, see code below).  Pin 6 and Pin 5 of the Arduino are connected to Channel 1 and Channel 2 of the Tek MDO3000.  The scope is put into XY Display mode.  Then like "magic" you have the world's most impractical motorcycle gear position indicator ever constructed!
    ----
    Like most of my projects this could not have been accomplished without the help of those much smarter than me posting examples and inspiration on the web.  I tried to credit them in my source code comments.  In the unlikely event you duplicate this project, please try to give credit where credit is due. 
    ----
    Source code is below.  I'm a hacker, not a SW Engineer.  Plus, I cobbled the code together in a few hours.  I know the code can be better written (much better written).

    /*
     * Oscilloscope GPI for Suzuki VSTROM DL1000
     *
     *  Created: Jun 2014
     *  
     *WhiskeTangoHotel.Com
     *        with special thanks to John M. De Cristofaro
     *        with special thanks to johngineer   
     *         (http://www.flickr.com/photos/johngineer/6496005491/sizes/z/in/photostream/)
     *
     */

    /* ****************************************************************************
    Circuit for both PWM ports:

                              R
    PWM OUT ----/\/\/\-----+------------ OUTPUT
                                        |
                                     === C
                                          |
                                     GND

    R = 10k
    C = 0.1uF

    **************************************************************************** */

    #define TRACE_DELAY2500  // trace delay in uS (start with 2500). making this longer will
         // result in a straighter drawing, but slower
         // refresh rate. making it too short will result
         // in an angular blob.

    #define X               6     // attach scope channel 1 (X) to pin 6
    #define Y               5     // attach scope channel 2 (y) to pin 5

    int gearvoltage = 1;     // define analog input A1 as ADC that monitors the ECM on the bike
    int gearval = 0;            // reading from gearvoltage ADC is converted into the gear to display.  1-6 and 7=N.  Set to 0 to get into M3 self Test Loop
    int i;                 // counter for M3 self test delay loop


    void setup()
    {
      pinMode(X, OUTPUT);
      pinMode(Y, OUTPUT);

      // The following sets the PWM clock to maximum on the Arduino(no CPU clock division)
      // DO NOT CHANGE THESE UNLESS YOU KNOW WHAT YOU ARE DOING!
      
      TCCR0A = (1<<COM0A1 | 0<<COM0A0 |// clear OC0A on compare match (hi-lo PWM)
    1<<COM0B1 | 0<<COM0B0 |// clear OC0B on compare match (hi-lo PWM)
    1<<WGM01  | 1<<WGM00);// set PWM lines at 0xFF

      TCCR0B = (0<<FOC0A | 0<<FOC0B |// no force compare match
    0<<WGM02 |// set PWM lines at 0xFF
    0<<CS02 | 0<<CS01 |// use system clock (no divider)
    1<<CS00 );

      TIMSK0 = (0<<OCIE0B | 0<<TOIE0 |
    0<<OCIE0A );  

      //All Serial statements in loops are for debug only.
      Serial.begin(9600);

    } // end void setup ***********************************************************


    void loop()  
    {

    // Main loop.  Reads the Gear Position Signal from the Vstrom ECM.  Displays X-Y to o'scope

    // x, y coordinates range from 0 to 255.  0,0 is lower left.  255,255 is upper right

    if (gearval == 0) {  // M3 Self Test Loop----------------------------
      int NUM_POINTS = 38;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("M3 Self Test Loop");
      Serial.println(NUM_POINTS);      
      // x coords for drawing the gear number
      unsigned char x_points[38] = {140, 140, 140,  90,  75,  60,  10,  10, 10, 40, 40,  40, 60, 90, 110, 110, 110, 140, 205, 250, 250, 230, 250, 250, 160, 160, 190, 190, 220, 220, 200, 200, 220, 220, 190, 190, 160, 160 };  
      // y coords
      unsigned char y_points[38] = {50, 110, 170, 170, 100, 170, 170, 110, 50, 50, 95, 140, 50, 50, 140,  95, 50,  50,   50,  50, 110, 115, 120, 170, 170, 140, 140, 150, 150, 130, 130, 100, 100,  80,  80,  90,  90,  50};

        unsigned char t;
        for (i = 0; i < 50; i++) //M3 self test delay loop  count of 100 is ~ 5 secs.
          {
            for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
            {
              analogWrite(X, x_points[t]);
              analogWrite(Y, y_points[t]);
              /*
              while (! Serial);
              Serial.println("t loop");
              Serial.println(t);       
              Serial.println(x_points[t]);
              Serial.println(y_points[t]);
              Serial.println("-----------");
              */     
      delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
            }  // end if t   
        }  //end unsigned    
    } // end M3 Self Test Loop -------------------------------------



    gearval = analogRead(gearvoltage); //read the ECM and covert to a gearval 1-6 or 7 for N


    if ( (gearval >= 0)   &&  (gearval < 346)  ) {gearval = 1; }
    if ( (gearval >= 347) &&  (gearval < 451)  ) {gearval = 2; }
    if ( (gearval >= 452) &&  (gearval < 600)  ) {gearval = 3; }
    if ( (gearval >= 601) &&  (gearval < 756)  ) {gearval = 4; }
    if ( (gearval >= 757) &&  (gearval < 886)  ) {gearval = 5; }
    if ( (gearval >= 887) &&  (gearval < 974)  ) {gearval = 6; }
    if ( (gearval >= 975) &&  (gearval < 1024) ) {gearval = 7; }

    //gearval = 0;

    while (! Serial);
    Serial.println("gearval is:");
    Serial.println(gearval);
    Serial.println("-----------");

    if (gearval == 1) {  // ----------------------------
      int NUM_POINTS = 12;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("1G Loop");
      Serial.println(NUM_POINTS);      
      // x coords for drawing the gear number
      unsigned char x_points[12] = {60, 90, 120, 120, 120, 120, 120, 60, 170, 120, 120, 60};  
      // y coords
      unsigned char y_points[12] = {180, 200, 220, 170, 130, 80, 30, 30, 30, 30, 220, 180};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */     
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t   
          
        }  //end unsigned    
    } // end Gear 1 loop -------------------------------------

    if (gearval == 2) {  // ----------------------------
      int NUM_POINTS = 19;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("2G Loop");
      Serial.println(NUM_POINTS);      
      // x coords for drawing the gear number
      unsigned char x_points[19] = {60, 85, 120, 150, 160, 140, 100, 60, 120, 170, 120, 60, 100, 140, 160, 150, 120, 85, 60};  
      // y coords
      unsigned char y_points[19] = {200, 215, 220, 200, 180, 130, 80, 30, 30, 30, 30, 30, 80, 130, 180, 200, 220, 215, 200};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 2 loop -------------------------------------

    if (gearval == 3) {  // ----------------------------
      int NUM_POINTS = 29;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("3G Loop");
      Serial.println(NUM_POINTS);      
      // x coords for drawing the gear number
      unsigned char x_points[29] = {60, 85, 120, 150, 160, 150, 120, 80, 120, 150, 160, 155, 120, 85, 60, 85, 120, 155, 160, 150, 120, 80, 120, 150, 160, 150, 120, 85, 60};  
      // y coords
      unsigned char y_points[29] = {200, 215, 220, 200, 180, 145, 130, 130, 130, 110, 80, 50, 30, 30, 50, 30, 30, 50, 80, 110, 130, 130, 130, 145, 180, 200, 220, 215, 200};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 3 loop -------------------------------------

    if (gearval == 4) {  // ----------------------------
      int NUM_POINTS = 19;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("4G Loop");
      Serial.println(NUM_POINTS);      
      // x coords for drawing the gear number
      unsigned char x_points[19] = {140, 140, 140, 140, 90, 65, 30, 80, 140, 180, 140, 80, 30, 65, 90, 140, 140, 140, 140};
      // y coords
      unsigned char y_points[19] = {10, 65, 110, 210, 160, 120, 65, 65, 65, 65, 65, 65, 65, 120, 160, 210, 110, 65, 10};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 4 loop -------------------------------------

    if (gearval == 5) {  // ----------------------------
      int NUM_POINTS = 21;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("5G Loop");
      Serial.println(NUM_POINTS);       
      // x coords for drawing the gear number
      unsigned char x_points[21] = {160, 90, 60, 61, 65, 140, 155, 155, 130, 85, 40, 85, 130, 155, 155, 140, 65, 61, 60, 90, 160}; 
      // y coords
      unsigned char y_points[21] = {200, 200, 200, 180, 120, 110, 80, 45, 20, 10, 30, 10, 20, 45, 80, 110, 120, 180, 200, 200, 200};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 5 loop -------------------------------------


    if (gearval == 6) {  // ----------------------------
      int NUM_POINTS = 29;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("6G Loop");
      Serial.println(NUM_POINTS);     
      // x coords for drawing the gear number
      unsigned char x_points[29] = {160, 140, 90, 61, 30, 30, 35, 65, 85, 130, 155, 155, 140, 65, 30, 65, 140, 155, 155, 130, 85, 65, 35, 30, 30, 61, 90, 140, 160};  
      // y coords
      unsigned char y_points[29] = {200, 210, 200, 180, 140, 105, 65, 15, 10, 20, 45, 80, 110, 120, 105, 120, 110, 80, 45, 20, 10, 15, 65, 105, 140, 180, 200, 210, 200};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 6 loop -------------------------------------

    if (gearval == 7) {  // 7 is for N ----------------------------
      int NUM_POINTS = 15;    // display output (trace) is defined by this many x/y coord. pairs
      while (! Serial);
      Serial.println("N Loop");
      Serial.println(NUM_POINTS);
      // x coords for drawing the gear number
      unsigned char x_points[15] = {50, 50, 50, 90, 140, 170, 170, 170, 170, 170, 140, 90, 50, 50, 50};
      // y coords
      unsigned char y_points[15] = {30, 120, 220, 160, 80, 30, 120, 220, 120, 30, 80, 160, 220, 120, 30};

        unsigned char t;
        {
          for(t = 0; t < NUM_POINTS; t++)// run through the points in x & y
          {
            analogWrite(X, x_points[t]);
            analogWrite(Y, y_points[t]);
            /*
            while (! Serial);
            Serial.println("t loop");
            Serial.println(t);       
            Serial.println(x_points[t]);
            Serial.println(y_points[t]);
            Serial.println("-----------");
            */
    delayMicroseconds(TRACE_DELAY);// wait TRACE_DELAY microseconds
          }  // end if t
          
        }  //end unsigned    
    } // end Gear 7 = N loop -------------------------------------
        
    }  // end void loop
    ----
    If you're still with us, thanks for the visit and check out our other 'stuff' at WhiskeyTangoHotel.Com.


    Arduino Based 60 Second Timer for Gym

    $
    0
    0

    -----
    This project is a good beginner Arduino project.  I wanted a digital timer in my gym that counted up from 00 to 60 seconds and reset.  Since I had a spare Arduino Nano and a dual 7-segment display (rescued from a guitar effect pedal) I decided to build my own instead of driving to Wal-Mart.
    -----
    If you are not interested in the details and just want to see the result, check out the video below.  If you watch more than the first 10-15 seconds then you should seriously consider getting hobby.  ;)
    -----
    The dual 7-segment display pinout looks like this.  The Arduino Nano has 14 digital outputs and you will need them all.  Note: You will have to disconnect D0 and D1 from the dual 7-segment display to get reliable program uploads to the Arduino because these pins double as the USB data connection to your PC.

    Not the best documentation, but hook up is pretty straight forward.  Each segment is labeled 'A' through 'G' with a "t" suffix for 'tens place' and an 'o' suffix for 'ones place'.  Next to each of the 14 labeled segments is the digital pin on the Arduino that that segment is connected to; D0 through D13.
    -----
    And here is the code.  Total project including wiring took a bit over two hours and I am a slow coder.
    /*
     **************************************
     ***** www.WhiskeyTangoHotel.Com  *****
     **************************************
     Project Name: Gym Counter Timer

     Start Date:  July 26, 2014

     Program Rev History and Notes:
     Simple timer to count up 60 secs then reset.
     LEDS are active LOW.  Low turns the segment on.
     LEDS came from old Zoom guitar effects pedal.

     NOTE:  DISCONNECT D0 AND D1 TO PREVENT U/L ERRORS

     ***************************************
     */

    //All 14 digi pins are needed as outputs to drive two 7-seg displays.  Assign aliases
    // standard a-g labeling on the 7-segs.  The decmial point (DP) is not used.
    int a_ones = 0;
    int b_ones = 1;
    int c_ones = 2;
    int d_ones = 3;
    int e_ones = 4;
    int f_ones = 5;
    int g_ones = 6;

    int a_tens = 7;
    int b_tens = 8;
    int c_tens = 9;
    int d_tens = 10;
    int e_tens = 11;
    int f_tens = 12;
    int g_tens = 13;

    int i;


    //**************************************

    void setup()  // Functions here
    {
      // Define all digi pins as OUTPUT
      pinMode(a_ones, OUTPUT);
      pinMode(b_ones, OUTPUT);
      pinMode(c_ones, OUTPUT);
      pinMode(d_ones, OUTPUT);
      pinMode(e_ones, OUTPUT);
      pinMode(f_ones, OUTPUT);
      pinMode(g_ones, OUTPUT);

      pinMode(a_tens, OUTPUT);
      pinMode(b_tens, OUTPUT);
      pinMode(c_tens, OUTPUT);
      pinMode(d_tens, OUTPUT);
      pinMode(e_tens, OUTPUT);
      pinMode(f_tens, OUTPUT);
      pinMode(g_tens, OUTPUT);

      // Arduino wakes up with pins low.  Set them HI to turn off the LEDS
      all_off();

      i = 200; // 200 'feels right'.  Self test count delay from "00" to "99"

      zero_ones();
      zero_tens();
      delay(i);
      all_off();

      one_ones();
      one_tens();
      delay(i);
      all_off();

      two_ones();
      two_tens();
      delay(i);
      all_off();

      three_ones();
      three_tens();
      delay(i);
      all_off();

      four_ones();
      four_tens();
      delay(i);
      all_off();

      five_ones();
      five_tens();
      delay(i);
      all_off();

      six_ones();
      six_tens();
      delay(i);
      all_off();

      seven_ones();
      seven_tens();
      delay(i);
      all_off();

      eight_ones();
      eight_tens();
      delay(i);
      all_off();

      nine_ones();
      nine_tens();
      delay(i);
      all_off();

      i = 1000; // Make this one second for count below

    }  //void setup

    //**************************************

    void loop()  //Loop Forever
    {

      for (int display_count = 0; display_count <= 59; display_count++){
        switch (display_count) {   // branch (switch) to case below and display the two digits
          case 0:
            zero_tens();
            zero_ones();
            break;

          case 1:
            zero_tens();
            one_ones();
            break;      

          case 2:
            zero_tens();
            two_ones();
            break;

          case 3:
            zero_tens();
            three_ones();
            break;

          case 4:
            zero_tens();
            four_ones();
            break;

          case 5:
            zero_tens();
            five_ones();
            break;      

          case 6:
            zero_tens();
            six_ones();
            break;

          case 7:
            zero_tens();
            seven_ones();
            break;

           case 8:
            zero_tens();
            eight_ones();
            break;

          case 9:
            zero_tens();
            nine_ones();
            break;      

          case 10:
            one_tens();
            zero_ones();
            break;

          case 11:
            one_tens();
            one_ones();
            break;

          case 12:
            one_tens();
            two_ones();
            break;

          case 13:
            one_tens();
            three_ones();
            break;      

          case 14:
            one_tens();
            four_ones();
            break;

          case 15:
            one_tens();
            five_ones();
            break;    

          case 16:
            one_tens();
            six_ones();
            break;
         
          case 17:
            one_tens();
            seven_ones();
            break;      

          case 18:
            one_tens();
            eight_ones();
            break;

          case 19:
            one_tens();
            nine_ones();
            break;

          case 20:
            two_tens();
            zero_ones();
            break;
     
          case 21:
            two_tens();
            one_ones();
            break;
       
          case 22:
            two_tens();
            two_ones();
            break;

          case 23:
            two_tens();
            three_ones();
            break;

          case 24:
            two_tens();
            four_ones();
            break;

          case 25:
            two_tens();
            five_ones();
            break;

          case 26:
            two_tens();
            six_ones();
            break;

          case 27:
            two_tens();
            seven_ones();
            break;

          case 28:
            two_tens();
            eight_ones();
            break;

          case 29:
            two_tens();
            nine_ones();
            break;

          case 30:
            three_tens();
            zero_ones();
            break;        
     
          case 31:
            three_tens();
            one_ones();
            break;
       
          case 32:
            three_tens();
            two_ones();
            break;

          case 33:
            three_tens();
            three_ones();
            break;

          case 34:
            three_tens();
            four_ones();
            break;

          case 35:
            three_tens();
            five_ones();
            break;

          case 36:
            three_tens();
            six_ones();
            break;

          case 37:
            three_tens();
            seven_ones();
            break;

          case 38:
            three_tens();
            eight_ones();
            break;

          case 39:
            three_tens();
            nine_ones();
            break;

          case 40:
            four_tens();
            zero_ones();
            break;

          case 41:
            four_tens();
            one_ones();
            break;

          case 42:
            four_tens();
            two_ones();
            break;

          case 43:
            four_tens();
            three_ones();
            break;

          case 44:
            four_tens();
            four_ones();
            break;

          case 45:
            four_tens();
            five_ones();
            break;

          case 46:
            four_tens();
            six_ones();
            break;

          case 47:
            four_tens();
            seven_ones();
            break;

          case 48:
            four_tens();
            eight_ones();
            break;

          case 49:
            four_tens();
            nine_ones();
            break;

          case 50:
            five_tens();
            zero_ones();
            break;      
     
          case 51:
            five_tens();
            one_ones();
            break;
       
          case 52:
            five_tens();
            two_ones();
            break;
       
          case 53:
            five_tens();
            three_ones();
            break;
       
          case 54:
            five_tens();
            four_ones();
            break;
       
          case 55:
            five_tens();
            five_ones();
            break;
       
          case 56:
            five_tens();
            six_ones();
            break;

          case 57:
            five_tens();
            seven_ones();
            break;

          case 58:
            five_tens();
            eight_ones();
            break;

          case 59:
            five_tens();
            nine_ones();
            break;    
       
        } // case switch statement
     
        delay(i); // i is set above to one second
        all_off();
     
      }  // for display_count loop

    }  //void loop


    // Function subroutines below to turn on and off the 7-segments
    // "_ones" is for the ONES place in the counter.  "_tens" is for TENS place in the counter
    void zero_ones() {
      // segments a b c d e
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
      digitalWrite(e_ones, 0);
      digitalWrite(f_ones, 0);
    } //zero_ones

    void one_ones() {
      // segments b c
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
    } //ones_ones

    void two_ones() {
      // segments a b g e d
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(g_ones, 0);
      digitalWrite(e_ones, 0);
      digitalWrite(d_ones, 0);
    } //two_ones

    void three_ones() {
      // segments a b g c d
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(g_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
    } //three_ones

    void four_ones() {
      // segments f g b c
      digitalWrite(f_ones, 0);
      digitalWrite(g_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
    } //four_ones

    void five_ones() {
      // segments a f g c d
      digitalWrite(a_ones, 0);
      digitalWrite(f_ones, 0);
      digitalWrite(g_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
    } //five_ones

    void six_ones() {
      // segments a f g c d e
      digitalWrite(a_ones, 0);
      digitalWrite(f_ones, 0);
      digitalWrite(g_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
      digitalWrite(e_ones, 0);
    } //six_ones

    void seven_ones() {
      // segments a b c
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
    } //seven_ones

    void eight_ones() {
      // segments a b c d e f g
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
      digitalWrite(e_ones, 0);
      digitalWrite(f_ones, 0);
      digitalWrite(g_ones, 0);
    } //eight_ones

    void nine_ones() {
      // segments a b c d e f g
      digitalWrite(a_ones, 0);
      digitalWrite(b_ones, 0);
      digitalWrite(c_ones, 0);
      digitalWrite(d_ones, 0);
      digitalWrite(f_ones, 0);
      digitalWrite(g_ones, 0);
    } //nine_ones


    void zero_tens() {
      // segments a b c d e
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
      digitalWrite(e_tens, 0);
      digitalWrite(f_tens, 0);
    } //zero_tens

    void one_tens() {
      // segments b c
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
    } //ones_tens

    void two_tens() {
      // segments a b g e d
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(g_tens, 0);
      digitalWrite(e_tens, 0);
      digitalWrite(d_tens, 0);
    } //two_tens

    void three_tens() {
      // segments a b g c d
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(g_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
    } //three_tens

    void four_tens() {
      // segments f g b c
      digitalWrite(f_tens, 0);
      digitalWrite(g_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
    } //four_tens

    void five_tens() {
      // segments a f g c d
      digitalWrite(a_tens, 0);
      digitalWrite(f_tens, 0);
      digitalWrite(g_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
    } //five_tens

    void six_tens() {
      // segments a f g c d e
      digitalWrite(a_tens, 0);
      digitalWrite(f_tens, 0);
      digitalWrite(g_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
      digitalWrite(e_tens, 0);
    } //six_tens

    void seven_tens() {
      // segments a b c
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
    } //seven_tens

    void eight_tens() {
      // segments a b c d e f g
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
      digitalWrite(e_tens, 0);
      digitalWrite(f_tens, 0);
      digitalWrite(g_tens, 0);
    } //eight_tens

    void nine_tens() {
      // segments a b c d e f g
      digitalWrite(a_tens, 0);
      digitalWrite(b_tens, 0);
      digitalWrite(c_tens, 0);
      digitalWrite(d_tens, 0);
      digitalWrite(f_tens, 0);
      digitalWrite(g_tens, 0);
    } //nine_tens

    void all_off()  {
      digitalWrite(a_ones, 1);
      digitalWrite(b_ones, 1);
      digitalWrite(c_ones, 1);
      digitalWrite(d_ones, 1);
      digitalWrite(e_ones, 1);
      digitalWrite(f_ones, 1);
      digitalWrite(g_ones, 1);

      digitalWrite(a_tens, 1);
      digitalWrite(b_tens, 1);
      digitalWrite(c_tens, 1);
      digitalWrite(d_tens, 1);
      digitalWrite(e_tens, 1);
      digitalWrite(f_tens, 1);
      digitalWrite(g_tens, 1);
    } // all_off
    -----
    Thanks for the visit!

    Testing the CREE 5000 Lumen XML U2 LED HeadLight

    $
    0
    0
    The CREE 5000 Lumen XML U2 LED HeadLight was a topic of discussion at Ride Dual Sport.  The product seemed well built from the pics and at less than $18 shipped it seemed like a must try.

    The unit comes complete with a the LED light, rechargeable battery (with a nice nylon case), handle bar mount, and charger.


    The light feels solid and well built.


    ----
    So how does it perform?  First off; it's bright.  Really bright!!!  There are three brightness setting, but honestly, the dimmest seems plenty bright.  I ran the light from full charge to dead on LOW and HIGH.  On LOW a 4.5 hour run time was observed.  A Keithley 2110 5 1/2 digit precision digital multimeter was used to record current draw.


    -----
    The battery pack puts out 8VDC, so don't connect the headlight straight to a 12VDC motorcycle or car battery without spending 60 cents for 7808 regulation.  


    -----
    Current shown by the Keithley 2110 with the unit in STANDBY mode; ~50 hours estimated.



    -----
    Current shown by the Keithley 2110 with the unit in LOW mode; 4.5 hours observed.


     -----
    Current shown by the Keithley 2110 with the unit in MEDIUM mode; ~2 hours estimated.  



    -----
    Current shown by the Keithley 2110 with the unit in HIGH mode; 1.5 hours observed.

     -----
    The unit is built well, works great, and is a fantastic value.

    Real Time Tracking of Aircraft with the Raspberry PI

    $
    0
    0
    Almost all the aircraft flying overhead broadcast tracking information on 1.09GHz via ADS-B.  ADS-B is an open protocol.  So what?  Well, it means you can decode it.  If you just interested in what information this RasPI set up can provide to you take a look at the pics below.  Keep reading if you are interested in duplicating the rig.
    -----
    The two graphs below show how many aircraft were tracked and how many positions were reported to FlightAware.com (more on this later):
    -----
    The daily history log that produced the graph above:
    -----
    The Polar/Spider chart below shows the direction and distance from your rig of the planes that were tracked.  This chart can help with antenna location:
    -----
    The rig will also give a real time status of the planes being tracked.  If longitude/latitude information is available from the plane the ground track will be plotted:
    ----
    Still interested?  Setting it up is pretty easy now thanks to satsignal and FlightAware.com; check out these sites if you want more detail or have a problem.   Anyone should be able to duplicate the build.  When I first took the project on things were not quite as straightforward; it was even kinda difficult....     It is now straightforward this blog entry has been edited down to just a few lines and some tips.  Seems like a lot of commands, but copy/paste makes will make the install fast.  Most of the info on this page comes from the two sites mentioned above so look there is you want details.
    -----
    - You'll need a Raspberry PI running Raspbian (NOOBS is fine) with always on internet connection.

    - You'll also need a Software Define Radio (SDR).  This SDR from Amazon is less than $10. It works fine, but a better antenna could help.  The included antenna will give ~100 mile range with good placement and look something like this:

    - Use a good solid USB power source for the RasPI as the SDR will need the juice to run reliably.
    - Goto FlightAware.com and get an account.  It's free.
    - Install some software to ready the SDR for 1.09GHz tune, decode the ADS-B signals, and display the planes the rig tracks in a text format.  This install is well documented at satsignal.eu , but below is is boiled down just to the install commands and give a few tips.  If you have issues (you probably will not) head to satsignal.eu and check into their details.

    $ sudo apt-get update
    $ sudo apt-get upgrade
    $ sudo apt-get install git-core
    $ sudo apt-get install git
    $ sudo apt-get install cmake
    $ sudo apt-get install libusb-1.0-0-dev  # there is a slip on the referenced page
    $ sudo apt-get install build-essential
    $ git clone git://git.osmocom.org/rtl-sdr.git
    $ cd rtl-sdr
    $ mkdir build
    $ cd build
    $ cmake ../ -DINSTALL_UDEV_RULES=ON
    $ make
    $ sudo make install
    $ sudo ldconfig

    Now plug in the USB SDR dongle.

    $ sudo ldconfig
    $ cd ~
    $ sudo cp ./rtl-sdr/rtl-sdr.rules /etc/udev/rules.d/
    $ sudo reboot
    $ rtl_test -t

    If you get this error:
       Found 1 device(s):
       0: Generic RTL2832U
       Using device 0: Generic RTL2832U
       Kernel driver is active, or device is claimed by second instance of librtlsdr.
       In the first case, please either detach or blacklist the kernel module
       (dvb_usb_rtl28xxu), or enable automatic detaching at compile time.
       usb_claim_interface error -6
       Failed to open rtlsdr device #0

    Then you will need to do this (commands below):

    $ cd /etc/modprobe.d
    $ sudo nano no-rtl.conf

         In the blank file you just created called 'no-rtl1.conf' put the following three lines and save the file:

    blacklist dvb_usb_rtl28xxu
    blacklist rtl2832
    blacklist rtl2830

    Rerun the test and all should be fine:

    $ rtl_test -t

    Now the SDR is set up.  Ready for dump1090 install:

    $ cd /home/pi/
    $ git clone git://github.com/MalcolmRobb/dump1090.git
    $ cd dump1090
    $ make

    $ ./dump1090 --interactive --net

    The command above means run dump1090.  
    •      --interactive means show the results on the RasPI screen. 
    •      --net means we plan on sending the tracking data to FlightAware.com
    •      if you want to see other dump1090 options use the --help switch
    After running dump1090 look for a spinning text type cursor in the upper right corner.  The spinning cursor means all is running fine.

    At this point the RasPI is tracking planes.  Double check that by looking at your on personal tracking map on the RasPI.  From a browser local to your RasPIs network goto:

        http://<local_IP_address_of_the_dump1090_RasPI>:8080
    for example
       http://192.168.1.6:8080
    -----
    To log your finding with FlightAware.com just head to their piaware install site.   FlightAware.com has done a great job making the piaware install simple so just follow their instructions.  

    After piaware gets installed and running you will get a welcome email from FlightAware.com saying your RasPI is logging and all is good.  If you suspect an issue take a look at the file /tmp/piaware.out for clues or ask questions on the FlightAware.com discussion forum.
    -----
    Seems like a lot of work, but just copy/paste the commands and you should be fine.  If you get into trouble satsignal and FlightAware.com are the sites to visit for help.

    Happy logging!!!

    Accessing Hackaday.Com with Dell Axim

    $
    0
    0
    Our good friends at RideDualSport were just about to throw away what they thought was a useless Dell Axim.  We quickly rescued it because it offered the perfect opportunity to stroll down memory lane and enjoy a simpler time before WPA encryption and all (and we mean all) the modern day joys of mobile computing.

    To get the Axim on the net was not terribly challenging technically, but was a test in patience.  Hackaday.Com has featured several of our projects and we knew they had a retro site perfect for the test.  Plus, they often post up examples of devices that hit their retro site and we are always interested in self promotion.


    The Dell Axims are sold cheap on eBay and for very good reason...
    -----

    Raspberry PI CPU Usage Tachometer

    $
    0
    0

    We had an old car tachometer laying around the labs (beats me why...) that seemed perfect for some type of project.  But what?   Since it is pretty common to see a car looking tachometer as a CPU usage meter on the desktop of a PC we decided to create a hardware version of this for the Raspberry PI.

    Basically what we are going to do is read the CPU usage on the Raspberry PI.  Then convert that CPU usage value into a corresponding frequency.  That frequency will be output via GPIO on the Raspberry PI pin 11 to drive the tachometer.
    ----
    First step is to characterize the tachometer and find out what type of frequencies made the needle move.  The Tekronix 3252C made easy work of that.

    ----
    The spreadsheet below shows what frequencies move the tachometer needle to what position.  
    We also used this spreadsheet to calculate a multiplier to adjust the CPU usage reading on the RaspPI to a 0 RMP to 8000 RPM reading on the car tachometer.  It's a pretty simple calculation and you can see how it is used in the Python source code below.
    -----
    The tachometer needs 12VDC to power it, but the input signal to move the tachometer needle needs to be 5VDC.  A 7805 voltage regulator solves that problem.  Also, to buffer the RasPI GPIO a 7404 was used to drive the RasPI signal into the tach.  The connection looks like this:
    ----
    Using some Python code the end result is this:

    In the video you can see how moving the mouse around on the Raspberry PI increases the CPU usage making the tachometer reading increase.  When the Chromium web browser is launched the CPU usage goes to 100% and the tachometer needle 'redlines'.
    -----
    The Python code is straightforward.  Drop us a line if you decide to duplicate the build!


    #
    # Program makes a simple car tachometer read CPU usage percentage.
    #
    # WhiskeyTangoHotel.Com - December 2014
    #
    # To run this program you must first do a one time install
    # of the following from the terminal commans line.
    #
    # sudo apt-get install python-pip python-dev
    # sudo pip install psutil
    # sudo apt-get install python-psutil
    # see: http://sourceforge.net/p/raspberry-gpio-python/wiki/PWM/ for PWM info

    import time
    import psutil
    import RPi.GPIO as GPIO
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(11, GPIO.OUT)# Pin 11 drives the tachometer

    #p = GPIO.PWM(channel, frequencyHz)
    p = GPIO.PWM(11, 200) # move the tach needle to about 1/2 scale
    #p.start(dc)   # where dc is the duty cycle (0.0 <= dc <= 100.0)
    p.start(50)
    print ''
    print 'SELF TEST: Tach to about 1/2 scale for 5 seconds...'
    time.sleep(5)   
    print '' 

    i = 0# just a counter
    adjust_cpu_to_hz = 3.8   # adjust_cpu_to_hz is calculated on the tach characterization spreadsheet.
    # Tektronix 3252C was used to feed input into the tach to create the
    # characterization spreadsheet.  Discover how many Hz needed to move needle.

    while(True): # loop forever  
    i = i + 1
    # read the RasPI CPU Usage. Store the value in var cpu_usage.
    cpu_usage = psutil.cpu_percent()
    # use adjust_cpu_to_hz to scale the cpu_usage result to Hz within the tach's range
    p = GPIO.PWM(11, cpu_usage * adjust_cpu_to_hz)  
    p.start(50)
    print 'Run #:', i, ' ', cpu_usage,'%', '   ', cpu_usage * adjust_cpu_to_hz,'Hz out to tach'
    time.sleep(3)  # allow x secs to give the CPU a breather
    p.stop()    # stop PWM (also end of while loop)

    input('Press return to stop:')  
    p.stop()    # stop PWM
    GPIO.cleanup()# Take out the trash
    ----

    Viewing all 133 articles
    Browse latest View live