Monday, September 15, 2014

Testing the CREE 5000 Lumen XML U2 LED HeadLight

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.

Saturday, July 26, 2014

Arduino Based 60 Second Timer for Gym


-----
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!

Saturday, July 5, 2014

Tektronix MDO3000 Motorcycle Gear Position Indicator

-----
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_DELAY 2500  // 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.

Tuesday, April 1, 2014

RasPI + RGB LED = Color of Twitter

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!

Sunday, February 23, 2014

Graphing Twitter Mentions with the Raspberry PI

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 (http://open.sen.se/).
-----
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!