Introduction: IR Tracking Turret With PIC and C

About: Texan living in NL. I like to make simple electronics projects and share them with the community.

This is a good learning project that is fun to build - I got a lot out of making it. I think this has been done quite a bit already, but I did not find a straightforward way of doing this with PICs. When I finally got it working, I thought I'd share it. It might be useful for someone that wants to learn or apply I2C, Interrupts and Callbacks, PWM, or Servo Control.

Basic Functionality

Detects and tracks an infrared light source. Pans or tilts when the light source moves away from the center of the frame, attempting to keep it in the center.

Main Components

  • Pixart IR Camera
  • PIC16F1503 (a 14-pin, 8-bit PIC microcontroller)
  • Voltage Regulator
  • Servos

Assumptions

  • You can solder and desolder through-hole components
  • You have a computer
  • You can read a data sheet

Take-Away Skills

  • 8-bit PIC programming in C using MPLAB X, XC8 and MPLAB X Code Configurator
  • I2C Protocol
  • Using microcontroller timers
  • PWM servo control, and getting maximum resolution in movement
  • Using an adjustable voltage regulator

You might also learn a little about

  • IR Wavelengths
  • Camera field of view
  • Using one power source for servos and microcontroller
  • Calibrating the Pixart camera module
  • Making something with room for expansion

No PCB etching required :) I made this with stripboard and just cut traces and added jumpers where needed. It is also doable on a small breadboard. My first prototype fit the voltage regulator and main board components on one 17x10 breadboard.

This project should cost less than 25 EUR even if you have no spare parts laying around.

There are unused pins on the PIC16F1503 and I've included male headers for all pins in the stripboard pcb, so in-circuit programming is possible and there is room for expansion (another servo, more sensors, rocket launchers...).

Step 1: Tools and Materials

Most of this stuff you already have. You can use any sort of proto-board you want, but I recommend the strip kind for this project since I designed the PCBs using them.

Tools

  • Soldering Iron
  • Desoldernig braid
  • Small hacksaw
  • Heatshrink tubing ~5mm
  • Pickit3-compatible programmer

Optional Tools

  • hobby knife
  • debug header for PIC16F1503

Materials

  • Solder
  • Stripboard PCB
  • 10x20cm female-female breadboard wires
  • 6x10cm female-female breadboard wires
  • Assorted jumper wires
  • 4x1.5v batteries and holder (AA recommended)
  • 3V coin battery (CR2430 or CR2450 recommended)

Optional Materials

  • Double sided tape
  • Electrical tape
  • Tic-Tac boxes
  • ESD component capsules

Step 2: Components

You can find a broken WiiMote for cheap,I got one from Game Over in Amsterdam for 1 EUR. Broken WiiMotes are usually easy to find and the camera module is almost always OK - it's the buttons that wear out.

Pixart Camera Board

    • 1.5x1.3cm pice of stripboard (6x5 holes, with 5 holes along the strip)
    • 6x1 male header
    • 3x2 or two 3x1 male headers
    • Something to pot the camera connections

    For the camera module

    Front of Camera (bottom view)

    7 5 3 1
    8 6 4 2

      • Remove it from the WiiMote - desoldering braid works best, but I did manage to remove two intact with a blowtorch :)
      • Clip pins 3 and 4 - we don't need them
      • Nudge pins 5 and 6 a little toward the center and nudge the other as necessary to make them line up with the pins on the 3x2 or two 3x1 headers
      • Solder the camera pins to the header pins
      • Test the continuity of the connections
      • Pot the soldered connections in sugru, silicone, etc. - or just carefully wrap it with a piece of tape :)
      • Done!

      Voltage Regulator Board

      • 2.4x1.2cm piece of stripboard (9x4, with 9 holes along the strip)
      • LM317
      • 240 ohm resistor (I used two 120 ohm in series)
      • 380 ohm resistor
      • .1 uF capcitor
      • 3300 uF capacitor (you can get this from the WiiMote as well, else anything comparable will do)
      • 4x1 male header

      Main Board

      • 4x4cm piece of stripboard (15x15 holes)
      • 2x 7x1 make headers
      • 2x 6x1 male header
      • 4x1 male header
      • PIC16F1503
      • 25MHz oscillator
      • Red LED
      • 1x 220 ohm resistor
      • 2x 2.2K ohm resistor
      • .1 uF capacitor

      Notes

      Headers: Right-angle or straight will work - I actually wish I had used right-angle headers on the main board to keep things flatter.

      Step 3: Design

      This was intended to be an inexpensive learning project with room for expansion, simple enough to put on a small breadboard, programmed and assembled without too much difficulty.

      Requirements

      • Tracks at least one beacon 1-4 meters away, and pans/tils to keep it in frame
      • Around a 20 EUR build
      • Relatively small and light (all boards, servos and wiring, without batttery ~40g)
      • Runs off 6VDC
      • Reacts quickly to movement
      • Broken into modules
      • Camera module board as small as possible
      • Room for expansion

      As a bonus to you, there is also room for improvement :)

      Why Stripboard?

      I did not feel like making one from scratch for this - since the circuit is not very complex I thought I would tr to use proto board. I had worked out the circuit to be pretty compact on the breadboard and wanted to move it easily to the PCB, so I chose stripboard. With a few cuts and some jumpers I was able to keep things within 4x4 cm for the main board.

      Why the WiiMote?

      The camera module in the WiiMote uses an I2C interface and does all the heavy lifting for you. It provides pixel coordinates for up to 4 IR beacons, so you can easily figure out how much to pan/tilt to center the target. It has a high frame rate - something like 50 fps, so it can quickly detect and react to rapid movement. You can also configure it when initializing it to tune it to your use.

      There are a lot of very informative sites out there that fully explain every aspect of this module. Finally - most broken WiiMotes have a fully functional camera inside and can be had for a buck or two. Cheap, readily available and does exactly what I needed for this build.

      Why 8-Bit PIC?

      They are powerful enough for this project, cheap and readily available in most regions. There is good support for both assembler and C. The development tools are all free. Programmers are reasonable. Plus, with the PIC16F1503 you can use code generation tools in MPLABX (Microchip's programming IDE). Unfortunately this PIC does not have built-in debugging capabilities - you need a header to do that. But with this project you should not need one - it is pretty straightforward, and a good intro project for someone past the blinky stage.

      Step 4: PCBs

      You can see where to make the cuts in the copper and where to place jumper wires in the images above. Granted it's not as nice-looking as an etched PCB, but it's was way quicker for me since I had already breadboarded it. I recommend putting the jumpers in first - especially the one under the pic :)

      For the jumpers I used some from a set I had for breadboards. They solder up nicely and are already cut to useful lengths. You can always make your own. In my build I jumped some of the connections with solder on the copper-side of the protoboard.

      Step 5: Servos (Pan/Tilt)

      If you don't have a pan/tilt assembly around, or a 3D printer to print one out, or the patience to build one before you test your project - use double-sided tape. I used tape on my prototypes and they survived being mounted on drones (and crashing with them), getting caught up in rotors, being dropped repeatedly, and crushed under groceries in my bag.

      If you go the tape route, once you join the two servos you should mount the lower one - a vise or clips work fine in a pinch. In the pic above, the servo is clipped to my desk lamp.

      Step 6: Programming

      The coding for this is pretty simple. there are only four methods to write. We'll get all the I2C signal negotiation from the I2C library we'll generate with the Microchip Code Configurator.

      1. initWiiCam(); // Only needed once to configure the camera
      2. readWiiCam(); // Asks the camera for position of the beacon
      3. updatePan(); // Updates the pan if required to center the beacon on the X axis
      4. updateTilt(); // Updates the tilt if required to center the beacon on the Y axis

      Install MPLABX

      So let's get started. First, download and install MPLABX from Microchip: http://www.microchip.com/pagehandler/en-us/family/...

      Create a Project

      Once installation is done, start it up and go to File>New Project

      This will take you through a wizard to configure our project.

      1. Choose Project
        • Categories: Microchip Embedded
        • Projects: Standalone Project
      2. Select Device
        • Family: Mid-Range 8-bit MCUs (PIC12/16/MCP)
        • Device: PIC16F1503
      3. Select Header
        • - Header: None (or AC244051* if you have it and want to use it)
          * If you use a debug header, you have to build the project for debugging to program the header
      4. Select Tool
        • PICkit3 or compatible tool
      5. Select Compiler
        • XC8
      6. Select Project and Folder
        • Name: whatever you want (ex. IR_Tracking_Turret)
        • Folder: default
        • Encoding: ISO-8859-1
        • Location: default

      Okay, project created. Now let's generate some source files.

      Microchip Code Configurator (MCC)

      This is a nice tool in MPLABX that lets you select and configure peripherals like timers, PWM generators and I2C communications. After your project is created go to Tools>Embedded>Microchip Code Configuratior

      A screen will come up with the pinout of the chip and pin assignments on the right, and selected and available peripherals on the left. In he upper center there will be a [Generate Code] button. Clicking it will generate the code and configuration you select in the MCC. Since there are no source code files yet, MCC will ask if you want it to generate a main.c file - click yes. We will need the following peripherals configured for our application:

      • TMR1 - 16-bit timer
      • TMR2 - 8-bit timer
      • PWM2 - PWM out on pin 7
      • PWM3 - PWM out on pin 11
      • MSSP - I2C Master, SDA on pin 9, SCL on pin 10

      In the list on the lower right, find and double-click the above peripherals to add the to the list of selected peripherals on the upper left. Once you've got them all, let's generate some peripheral support code.

      System

      • System Clock Select: INTOSC
      • Frequency Select>Internal Clock: 16MHz_HF
      • CONFIG1>MCLR Pin Function Select [MCLRE OFF]

      TMR1

      • Clock Source: FOSC/4
      • Prescaler: 1:8 Timer
      • Period: 100ms
      • Enable Interrupt: check
      • Callback Function Rate: 30x (3s) - timeout for re-centering the turret

      TMR2

      • Prescaler: 1:64
      • Timer Period: 4ms
      • Start TImer after Initialization: check
      • Enable Timer Interrupt: check
      • Callback Function Rate: 5x (20ms) - for driving the servos, most servos will also work with a 12ms period.

      PWM2, PWM3

      • Enable PWM: check
      • Enable PWM Pin Output: check
      • PWM Polarity: Inverted (period 4ms)

      MSSP::I2C

      • MASTER Enable I2C: checked
      • Enable SM Bus Output: checked
      • Slew Rate Control: High Speed SDA
      • Hold Time: 100ns
      • I2C Clock Frequency: 400KHz
      • Slave Address: 7-bit
      • Read Buffer Size: 10 - depending on how many bytes you expect back from the device you are talking to, in this case, for the simplest configuration, the Pixart camerawill return 10 bytes.
      • Write Buffer Size: 2 - we will talk to the Pixart camera 2 bytes at a time.

      Adding your Own Code

      Now the most of the code for the project is generated, but we still have to declare some constants and variables and add some additional code, including four the methods mentioned above.

      main.c

      Find this line:

      /*
                               Main application
      */

      Add the following code beneath it

      #define SLAVE_I2C_GENERIC_RETRY_MAX     100
      I2C_MESSAGE_STATUS status = I2C_MESSAGE_PENDING;
      // Wii IR Camera Initialization Settings
      static const uint8_t    wiiCameraAddress = 0x58;
      static const uint8_t    wiiCameraReadCmd = 0x37;
      <p>static const uint8_t wiiCamInitData[14][2] = {{0x30, 0x01},
      /* Sensitivity config Block A */ 
      {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00},  
      {0x06, 0x90}, // p0: MAXSIZE: Maximum blob size. Wii uses values from 0x62 to 0xc8  
      {0x07, 0x00},  
      {0x08, 0x41}, // p1: GAIN: Sensor Gain. Smaller values = higher gain 
      /* Sensitivity config Block B */ 
      {0x1A, 0x40}, // p2: GAINLIMIT: Sensor Gain Limit. Must be less than GAIN for camera to function.  
       {0x1B, 0x00}, // p3: MINSIZE: Minimum blob size. Wii uses values from 3 to 5 </p><p>/* Mode config */ 
       {0x33, 0x01}, // mode: 1=basic, 3=extended, 5=full  
       {0x30, 0x08}};</p>
      uint16_t        DcPan = 630;
      uint16_t        DcTilt = 630;
      uint8_t         writeBuffer[2], cmdBuffer[1], readBuffer[10];
      uint8_t         *i2cData;
      uint16_t        b1x, b1y;
      void initWiiCam(void) {
          for (int cnt=0;cnt<14;cnt++) {
              i2cData = (uint8_t *)&wiiCamInitData[cnt];
              for (int i=0; i<2; i++) writeBuffer[i] = 0;
              writeBuffer[0] = *i2cData++;
              writeBuffer[1] = *i2cData++;
              I2C_MasterWrite(writeBuffer,2,wiiCameraAddress,&status);
              while (status == I2C_MESSAGE_PENDING);
          }
      }
      void readWiiCam (void) {
          // Read Wii Camera data
          i2cData = (uint8_t *)&wiiCameraReadCmd; // initialize the source of the data
          cmdBuffer[0] = 0;
          cmdBuffer[0] = *i2cData++;
          I2C_MasterWrite(cmdBuffer,1,wiiCameraAddress,&status);
          // wait for the message to be sent or status has changed.
          while (status == I2C_MESSAGE_PENDING);
          //__delay_ms(3);
          for (int i = 0; i < 10; i++) readBuffer[i] = 0x00;
          i2cData = (uint8_t *)&readBuffer;
          I2C_MasterRead( i2cData,10,wiiCameraAddress,&status);
          while (status == I2C_MESSAGE_PENDING);
          if (readBuffer[0] != 0xFF && readBuffer[0] != 0x00) {
              b1x = ((readBuffer[2] & 0x30) << 4) + readBuffer[0];
              b1y = ((readBuffer[2] & 0xC0) << 2) + readBuffer[1];
              TMR1_CallBackReset();
          }
          else {
              b1x = 0x00;
              b1y = 0x00;
          }
      }
      void updatePan(void) {
          // Adjust pan and tilt servos
          if ((b1x == 0x00) || (b1x == 0xFF)) return;
          if (b1x > 522)      DcPan += (b1x-522)/(20*3);
          if (b1x < 502)      DcPan -= (502-b1x)/(20*3);
          if (DcPan > 875)    DcPan = 875;
          if (DcPan < 375)    DcPan = 375;
          PWM2DCH = DcPan >> 2;
          PWM2DCL = DcPan << 6;
      }
      void updateTilt(void) {
          if ((b1y == 0x00) || (b1y == 0xFF)) return;
          if (b1y > 394)      DcTilt -= (b1y-394)/(20*3);
          if (b1y < 374)      DcTilt += (374-b1y)/(20*3);
          if (DcTilt > 875)   DcTilt = 875;
          if (DcTilt < 375)   DcTilt = 375;
          PWM3DCH = DcTilt >> 2;
          PWM3DCL = DcTilt << 6;
      }

      Enable interrupts and add the following code to the main function:

      void main(void) {
      	// initialize the device
      	SYSTEM_Initialize();
      	// Enable the Global Interrupts
      	// When using interrupts, you need to set the Global and Peripheral Interrupt Enable bits
      	// Use the following macros to:
      	INTERRUPT_GlobalInterruptEnable();
      	// Enable the Peripheral Interrupts
      	INTERRUPT_PeripheralInterruptEnable();
      	// Disable the Global Interrupts
      	//INTERRUPT_GlobalInterruptDisable();
      	// Disable the Peripheral Interrupts
      	//INTERRUPT_PeripheralInterruptDisable();
      	initWiiCam();
      __delay_ms(10);
      while (1) {        
      	readWiiCam();         
      	updatePan();         
      	updateTilt();         
      	__delay_ms(10);     }
      }
      /**  End of File */

      tmr2.c

      Servos use a .5 to 2.5 ms pulse in a 20ms period to determine their target position. Our configuration gives TImer2 a 4ms period, which is great for giving us a lot of precision within that period, but too short for servo use. So we use a callback that executes every 5th time Timer2 overflows. We keep PWM disabled until the 5th pass, giving us a 20ms period with a higer precision pulse length. This gives us smoother movement in tracking and better response to small movements. Go down to the Interrupt Service Routine (ISR) and Callback functions and edit them to look as follows.

      void TMR2_ISR(void) {
          static volatile unsigned int CountCallBack = 0;<br>    // clear the TMR2 interrupt flag
          PIR1bits.TMR2IF = 0;
          TRISCbits.TRISC5=1;
          TRISAbits.TRISA2=1;
          TRISCbits.TRISC3=1;
          // callback function - called every 5th pass
          if (++CountCallBack >= TMR2_INTERRUPT_TICKER_FACTOR) {
              // ticker function call
              TMR2_CallBack();
              // reset ticker counter
              CountCallBack = 0;
          }
          // add your TMR2 interrupt custom code
      }
      
      void TMR2_CallBack(void) {
          // Add your custom callback code here
          // this code executes every TMR2_INTERRUPT_TICKER_FACTOR periods of TMR2
          TRISCbits.TRISC5=0;
          TRISAbits.TRISA2=0;
          TRISCbits.TRISC3=0;
      }

      tmr1.c

      I just added this to recenter the turret if it does not see anything for 3 seconds. We need to declare the CountCallBack variable as global. Find this line:

      volatile uint16_t timer1ReloadVal; 

      Add ths line beneath it:

      volatile unsigned int CountCallBack = 0;

      Now go down to the Interrupt Service Routine (ISR) and Callback functions and edit them to look as follows.

      void TMR1_ISR(void) {
      //static volatile unsigned int CountCallBack = 0;
      // Clear the TMR1 interrupt flag
          PIR1bits.TMR1IF = 0;
          TMR1 += timer1ReloadVal;
          // callback function - called every 30th pass
          if (++CountCallBack >= TMR1_INTERRUPT_TICKER_FACTOR)    {
              // ticker function call
              TMR1_CallBack();
              // reset ticker counter
              CountCallBack = 0;
          }
          // Add your TMR1 interrupt custom code 
      }
      
      void TMR1_CallBack(void) {
          // Add your custom callback code here
          const uint16_t DcTiltReset = 630;
          const uint16_t DcPanReset = 630;
          PWM3DCH = DcTiltReset >> 2;
          PWM3DCL = DcTiltReset << 6;
          PWM2DCH = DcPanReset >> 2;
          PWM2DCL = DcPanReset << 6; 
      }

      tmr1.h

      Find this line

      void TMR1_CallBack(void);

      Add this line beneath it

      void TMR1_CallBackReset(void);

      Now all the code is in place and w can build and program the device.

      Clean and Build Project, then Make and Program Device. The PIC is ready to plug into the socket on the PCB.

      Step 7: Test and Tweak

      Test Rig

      The easiest way to test this is to assemble the servos together as in step 5, and attach the camera board with a little double-sided tape. It might help to first mount the camera board on a bit of plastic (Tic Tac box) to give you a good surface to tape to the tilt servo.

      Testing

      The easiest beacon is an IR remote, but since those are modulated, performance is not as good. The Pixart camera seems to respond better to 940-950 nm IR LEDs, which you can just pinch around a coin battery. I did this wqith very good results using a 940 nm LED with a 90 degree visibility cone. To improve omnidirectional visibility I also clustered 3 of them in parallel as in the image above.

      Pretty much an IR source will work indoors, and you can tweak camera sensitivity to get the best results with your setup. Beware of incandescent lights, candles, cigarettes and sunny windows - they might distract the turret until you have tuned the configuration. You might also consider using the IR pass filter from the WiiMote, or from another IR sensor (from a broken TV maybe). Best bet is to have a low-pass optical filter to match your beacon LEDs, but this is supposed to be a cheap project :)

      Try powering up your test rig. It should first center itself, then wait for an IR source. Step back about 10 feet or so and use the beacon. The servos should move - if they are not moving correctly, try switching the pan and tilt plugs on the main board.

      Tweaking

      Pixart Camera Config

      This link will save you a lot of time - I think all you need to know about the configuration and some recommended settings are there. http://wiibrew.org/wiki/Wiimote

      I'll briefly relate my experience here.

      • I used the basic output setting which gives you 10 bytes of positioning information for up to 4 blobs. There are two other levels which will get you additional info like size and intensity (12 and 18 byte payloads, respectively - you would have to increase the I2C read buffer accordinly), but I did not use that here.
      • Tune blob size threshholds to filter out unwanted interference from windows, fireplaces and incandescent lights.
      • Keeping the maximum blob size small is a good start.
      • Higher gain means it will be more sensistive to interference from reflections, other IR sources, etc., but provides better accuracy in positioning.

      I got pretty good results indoors (and out on a cloudy day) with the following configuration and the IR pass filter that comes in the WiiMote.

      static const uint8_t wiiCamInitData[14][2] = {{0x30, 0x01},<br>
      /* Sensitivity config Block A */
      {0x00, 0x00}, {0x01, 0x00}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00}, 
      {0x06, 0x90}, // p0: MAXSIZE: Maximum blob size. Wii uses values from 0x62 to 0xc8 
      {0x07, 0x00}, 
      {0x08, 0x41}, // p1: GAIN: Sensor Gain. Smaller values = higher gain 
      /* Sensitivity config Block B */
      {0x1A, 0x40}, // p2: GAINLIMIT: Sensor Gain Limit. Must be less than GAIN for camera to function. 
      {0x1B, 0x00}, // p3: MINSIZE: Minimum blob size. Wii uses values from 3 to 5 
      /* Mode config */
      {0x33, 0x01}, // mode: 1=basic, 3=extended, 5=full 
      <br>{0x30, 0x08}};

      Servo Turn Rate

      The camera returns coordinates on a 1028x768 x/y plane. It has a FOV of about 33 degrees across and 23 degrees high. It provides coordinates on a 1024x768 grid, so one degree is about 30 pixels. Because of the callback trick we used in tmr2.c we have 9 bits of precision for 4ms (or about 320 steps from 0.5ms to 2.5ms across the 180 degrees of servo travel) with the servo signal. Incrementing the CCPR for a PWM will affect the position by about 0.6 degrees.

      To prevent jitter we want to specify a window that is considered center so tracking is smoother and not hyper-sensitive. In this case, we have a window 20x20 pixels that is considered center. You can play with the "center" box size to get results to suit your needs.

      If the coordinates returned by the camera are outside of this window, we pan or tilt in that direction. I divide the pixel difference by (20*3) to smooth it out and prevent the nodding effect if you over-correct. The Camera is returning coordinates around 60 times a second here, so we need to smooth out the pan/tilt to keep it from jering all over the place.

      875 and 375 are the PWM values that correspond to 0.5 and 2.5 ms periods on the servo signal (extreme left to extreme right on a servo).

      void updatePan(void) {
      	if ((b1x == 0x00) || (b1x == 0xFF)) return;     
      	if (b1x > 522)      DcPan += (b1x-522)/(20*3);     
      	if (b1x < 502)      DcPan -= (502-b1x)/(20*3);     
      	if (DcPan > 875)    DcPan = 875;     
      	if (DcPan < 375)    DcPan = 375;     
      	PWM2DCH = DcPan >> 2;     
      	PWM2DCL = DcPan << 6; 
      }
      void updateTilt(void) {     
      	if ((b1y == 0x00) || (b1y == 0xFF)) return;     
      	if (b1y > 394)      DcTilt -= (b1y-394)/(20*3);     
      	if (b1y < 374)      DcTilt += (374-b1y)/(20*3);     
      	if (DcTilt > 875)   DcTilt = 875;     
      	if (DcTilt < 375)   DcTilt = 375;     
      	PWM3DCH = DcTilt >> 2;     
      	PWM3DCL = DcTilt << 6; 
      }

      IR Filter

      Using the IR filter from the WiiMote is a good idea if it is not too scratched up. Else you can find them in all kinds of discarded electronics.

      IR LED

      940-950 nm is recommended. 880 also works, but not as well a distance.

      Common Issues

      Servo Back-and-Forth (Over-correcting)

      Try dividing DcTilt or DcPan by a larger number to smooth out panning.

      Gets "stuck"

      The servos might be drawing too much power - try using a larger capacitor on the voltage regulator.

      Does not react

      Might be in continual reset - the servos might be drawing too much current to keep the camera or IC from resetting. Check all your connections and see if the servos center when you turn it on.

      Constant resetting

      Low battery usually (if not one of the above).

      Step 8: Assembly

      Ok, here you are pretty much on your own. The pic above shows one of the early Tic-Tac box prototypes I made. You just need some kind of enclosure to hold the main board, voltage regulator and batteries. Then you can mount the pan/tilt assembly on the enclosure.

      Be sure the wires you use to connect the camera board to the main board are long and flexible and leave enough space around it to allow free movement.

      Here's a couple videos of TicTac1 tracking a lighter. It's a little jerky because this was before I used the timer2 callback to get higher precision in movement.

      Step 9: Extending Functionality

      There area few unused pins on the PIC, so you still have an additional PWM, Clock Out, ADC, etc. to play with is you want to extend functionality to do more. You can also add more things to the I2C bus as long as they have different addresses.

      For more information on the PIC16F1503: http://www.microchip.com/wwwproducts/Devices.aspx?...

      Check out the data sheet to see what peripherals are available on the unused pins. I'll try to add more to this instructable as I think of more info to add. If you have any questions or feedback, I will respond and try to answer them or direct you to a better source of info.