Sunday, December 28, 2014

DIY Antenna Improves RasPI ADS-B Reception Range




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

Once we learned about this our focus was set on building a rig to track overhead aircraft using the Raspberry PI.  You can read all about the setup here.
----
The Software Defined Radio (SDR) we used comes with a small whip style antenna.  Fine for testing purposes, but we wanted to extend our reception range.  

The FlightAware forums turned us onto what looked like a simple low cost way to DIY a small quarter wave antenna that had proven results.  The antenna design is by "atouk" and documented in a .PDF titled "My First ADS-B Antenna".  The design is so straight forward you really only need to go off the dimensioned line drawing above, but the "My First ADS-B Antenna" document is worth the download.
----
It took about ten minutes to build the antenna.  We went through a three step process to gauge it's effectiveness.
  • Step 1:  Chart reception with the cheapie stock OEM whip antenna placed in a closet on the 2nd floor.
  • Step 2: Chart reception of the DIY quarter wave antenna placed in the exact same location as the OEM whip in Step 1.
  • Step 3: Chart reception of the DIY quarter wave antenna raised into the attic (actually only a few feet higher)
-----
Here are the Step 1 (OEM whip) results.  Click on image to enlarge:
-----
Here are the Step 2 (DIY quarter wave, same position as QEM whip) results:

Okay, we show improvement!  Notice the 'spider' chart outer rings are showing aircraft.  Also, the sharp rise in plane count on December 14th.
-----
Here are the Step 3 (DIY quarter wave, moved to attic) results:

Moving to the attic yielded only a slightly higher plane count on the daily graph.  But check out the  the 'spider' chart;  it does seem to indicate better coverage.
-----
The DIY quarter wave antenna is absolutely an improvement over the SDR OEM whip antenna and well worth the very small time and cost investment.  Our roof decking has a 'shiny stuff' radiant barrier on it.  The thought is the radiant barrier may be acting like a big metal blanket thrown over the antenna.  If we moved the antenna outdoors there would certainly be more improvement.  But for now our DIY quarter wave antenna will rest happily in the attic helping the Raspberry PI collect ADS-B data 24/7 and report it to FlightAware.
-----
Thanks for the visit and let us know how your rig improves!

Thursday, December 25, 2014

Tektronix MDO3000 as a MP3 Player


-----
A friend of mine got a Pono music player this Christmas.  The claimed advantage of the Pono player vs other music players is better sound fidelity.

So what does the Pono player do different?  Normal CD music is sampled at 44,100 times per second (44.1Ks/S) and 16-bit resolution.  The Pono is capable of playing music with an increased sample rate of 192,000 times a second (192Ks/S) and 24-bit resolution. A quick web search yields page after page of audiophiles debating these fidelity claims and we will leave that discussion to the experts.
------
Since Pono advocates see benefits in increasing sample rate from 44,100 times per second to 192,000 times a second, then just think about how awesome really turning up the sample rate would be!!!  But how?  Enter the Tektronix MDO3000 which can sample at an amazing 2,500,000,000 (2.5Gs/S) times per second and that's a lot!
 -----
The Tektronix MDO3000 is a benchtop instrument that has some truly amazing capabilities that push it way beyond being just an oscilloscope.  One of these features is the Arbitrary Function Generator (AFG) that can capture signals from any of the analog channels and play them back on command.
----
In the video below, we set the Tektronix MDO3000 to sample at a lazy 1,000,000 times per sec (1Ms/S).  Then we use a PC to play a MP3 music file for capture into the instrument's waveform memory via analog channel 1.
-----
Next, with a few button clicks the music waveform we just captured is loaded into the instrument's AFG to make it ready for playback.  Simple.
-----
The Tektronix MDO3000 AFG output is BNC connector on the back of the instrument.  We connect to the AFG and plug into a speaker.  The result: The most impractical (and expensive) MP3 player available, but with an impressive sample rate many, many times higher than the Pono player.
-----
This whole experiment means nothing, of course.  We did it simply because "we could".  If you do decide to go with a Pono (or a Tektronix MDO3000) as a music player remember that much of the sound quality is determined at the time of the original recording and not just at playback.  Also your headphones, speakers, listening room, ears, etc...
-----

Saturday, December 13, 2014

Propagation Delay of a 74HC04N Hex Inverter

-----
It takes some time for an electrical signal to travel down a wire or through a logic gate.  Not much time, but some.  It's called propagation delay (tpd) or "prop" delay.  We had a 74HC04N on the breadboard from our recent Rasperry PI CPU Usage Tachometer project and decided (for reasons unknown) to measure the prop delay of this chip.  The 74HC04N is a simple IC that turns a logic "0" into a "1" and turns "1" into a "0".  It has "six" gates to "invert" digital signals; thus the name "hex inverter".

----
For our measurement setup we used the Tektronix MDO4104B-6 oscilloscope.  It has plenty of what it takes to make this simple prop delay measurement and we had one handy. Since passing through one gate inverts a digital signal, passing the signal through two gates (or any even number of gates) will invert that signal back to it's original logic level.

Here is a 5VDC signal propagating though two gates.  The yellow trace is the input and the blue trace is the output after passing through two gates.  Using the scope cursors we measure the prop delay at 12nS  (nS or nanosecond = 10EE-9 seconds).  That's an average of 6ns per gate.
-----
Here is a 5VDC signal propagating though all six gates.  Again, the yellow trace is the input.  The blue trace is the output.  The scope cursors measure the total prop delay at 33.2nS (as well as some signal degradation from loading) for an average of 5.53nS per gate.
-----
Is that prop delay good or bad?  Well, we have to consult the device datasheet.  For a 5VDC setup NXP specs their 74HC04N at 7ns per gate typical, so our chip is beating the spec!  As far as it being good or bad depends on your application.  For most low speed hobby projects prop delay isn't even a consideration.  If you are designing high speed circuits then race conditions and prop delay specs become important to your design.  
-----
UPDATE: Special thanks to all the .EDU sites for checking in!

Thursday, December 11, 2014

Raspberry PI CPU Usage Tachometer


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

Friday, October 24, 2014

Accessing Hackaday.Com with Dell Axim

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.
----
Later that same day....

-----

Sunday, October 5, 2014

Real Time Tracking of Aircraft with the Raspberry PI

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 are 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 (http://192.168.1.6:8080):
----
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 with some ambition 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....    The build is now more mature allowing this original blog entry to be edited down to just a few command lines and some tips.  It 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.
-----
EDIT November 1, 2014: Getting your RasPI running is now nearly 'brain dead' simple.  See this link at FlightAware.Com
-----
- 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 it is boiled down just to the install commands and 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
$ 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!!!

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 a 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.