CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Wireless Dual Analog Joystick Controller with an Xbee Module

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Kerron



Joined: 16 Nov 2010
Posts: 5

View user's profile Send private message

Wireless Dual Analog Joystick Controller with an Xbee Module
PostPosted: Tue Dec 28, 2010 3:58 pm     Reply with quote

This is a Dual Analog Joystick Wireless Controller which provides incremental Remote Control Functions for a Robot that I designed that WORKS VERY WELL!!!!!!!!.

Full Project Code. Robotic Rover & Controller Firmware Code

[url] https://docs.google.com/open?id=0BxR63iu4EZXFNzM4NjNmODMtOGVlZi00ZDE4LWEzMGQtMDVlMzcwMGMxNjRh

[url] http://www.youtube.com/watch?v=tuVESl7Iz60

[url] http://www.youtube.com/watch?v=Q17Afg1u7-4

This Project uses the Xbee-Pro-60mW, PIC18F258, 2 Joysticks from a PlayStation 2 Controller & Runs on a 9V Battery

It consists of a dual analog joystick on a PCB board that was removed from a Play Station 2 controller and is wired to 4 analog ports of on a PIC18F258 Microprocessor "or any 28pin PIC with ADC & UART". Each joystick has 2 variable resistors in which one side was connected to +5V & the other to Gnd. The Joystick has a push button which requires a pull up resistor which then is also wired to the PIC Microprocessor . And there are two addition push buttons added to this project. An Xbee RF module is then wired to the PIC TX & RX lines. This project only uses the Xbee module as a transmitter in the future it will be a transceiver There is also a buzzer that generates tones & two leds one white & the other is a bi color red green led.





Dual Analog Joystick Wireless Controller





Board





Robot Rover Project





How The Software Works!!

1) Wait for Xbee to connect to the other Xbee module. Flash the bi color red while it waits 10 seconds. Then Flash Bi color led & play tones.

2) Calibrated Joysticks "find center"
if the joystick is pushed away from center while in calibration a default stored value will be used. if not it will zero out center "more accurate"

MAIN LOOP STARTS

1) Read Both joysticks on the ADC
Read_Joystick();
Then Subtract calibration values from it

2) Do Trig Calculations
Trig_Calc();
R=Sqrt(x2+y2) & Theta=ArcTan(Y/X)
"Cartesian to Polar Conversion"
Theta is where the joystick is pointing & R is magnitude
when greater that a certain magnitude "threshold' get theta
thats the idea!!! Magnitude value is useful for incremental control

3) Check to see if any of the 4 push buttons were pushed

4) Format & Transmit Data over the Air if Flag A = 1 "any activity"

5) Check for inactivity (Flag A = 0 ) & increment counter.
If counter expires put system into sleep mode to save battery power.
The remote can be awaken by pushing either of the push buttons which - triggers and external interrupt.

BACK TO TOP OF THE LOOP

The Transmitted Data Frame Example:


+$$MOVE:U,45,D,20,0,0,0,0* used for my robotic rover project

+$$ is the Header MOVE is the command type for the robot "remote control & camera pan & tilt control simultaneously". Then joystick 1 direction, Joystick1 Magnitude, then joystick 2 direction, Joystick2 Magnitude, then the four push buttons status "toggle" then a '*'\r as a stop sequence.

This can be customized for your project and Encryption & Check Sum can be easily implemented. For remote control you want your data frames to be fast and short if possible.


Memory usage Rom=34% RAM=6% - 10% There allot space left on the Chip



Main Source Code

Code:

#include<18f258.h>
#device ADC=10
#fuses HS,NOPROTECT,NOWDT,PUT
#include<stdio.h>
#include <STDLIB.H>
#include<MATH.H>
#use delay(clock=20000000)
#use Rs232 (baud = 57600, xmit= PIN_C6,rcv=PIN_C7,stream=Data)
#include<Functions.C>
int1 wake_sleep=0;
#zero_ram
main() // Main starts
{
delay_ms(10);
for ( i=1;i<=80;++i) //Flash Red Led while XBEE Device connects
   {
    Bi_Led_OFF;
    delay_ms(90);
    Bi_RED_ON;
    delay_ms(10);
   }
delay_ms(1);
for ( i=1;i<=5;++i) // Sound Sequence
   {
    Bi_Green_ON;
    generate_tone (4000,50);
    Bi_RED_ON;
    generate_tone (7000,50);
   }
Bi_Green_ON;
setup_adc_ports(ALL_ANALOG); //Setup analog ports
setup_adc( ADC_CLOCK_INTERNAL); //Setup analog ports

Inactivity =  0 ;
Calibration();

fprintf(Data,"\r\n");
fprintf(Data,"\r\n");
fprintf(Data,"\r\n");
fprintf(Data,"\r\n POWERED UP!!!");
fprintf(Data,"\r\n Software was compiled on ");
printf(__DATE__);
fprintf(Data,"\r\n Designed by Kerron Manwaring");
fprintf(Data,"\r\n Email: [email protected]");
fprintf(Data,"\r\n Phone: (914)-830-3542");
fprintf(Data,"\r\n");
fprintf(Data,"\r\n+$$ECHO\r\a");
delay_ms(100);
fprintf(Data,"\r\n DATA FRAME BELOW!!!");
fprintf(Data,"\r\n");

Bi_Led_OFF;
do {               // Main Loop Starts
 do{               // Loop while Inactivity counter is less the 5000
   Read_Joystick();// Read joystick analog port
   Trig_Calc();    // Do Trig Math
   Actions();      // Check for events "buttons pressed Joystick moved
   Transmit();     // Format & Transmit Serialy to Xbee module, if "Flag A=1  activity"
}While(Inactivity++ != 2000); // if inactive for some time "Inactivity == 2000 cycles" Enter into sleep mode
Inactivity=0; // clear timer
for ( i=1;i<=3;++i)// Inactivity sound seguence & Led flashing
   {
   Bi_Green_ON;
   generate_tone (2000,500);
   Bi_RED_ON;
   }
White_LED_ON;
generate_tone (1000,500);
White_LED_OFF;
Bi_Led_OFF;
fprintf(Data,"\r\n Entering Sleep Mode!!"); // To verify on hyperterminal
fprintf(Data,"\r\n");
fprintf(Data,"+$$AUTO*\r"); // Robot Command Back to Autonomous "Controller is Sleeping" off

enable_interrupts(GLOBAL); // Enable external interrupts
EXT_INT_EDGE(L_to_H);
enable_interrupts(INT_EXT);

enable_interrupts(GLOBAL); // Enable external interrupts
EXT_INT_EDGE(L_to_H);
enable_interrupts(INT_EXT1);
delay_ms(100);
wake_sleep=1; // wake_sleep Flag set to = 1 sleep
sleep(); // Actual Sleep function

}while(true);  // Main loop
}

#INT_EXT
void ext_isr()
{
disable_interrupts(INT_EXT);  // Button pushed
if (wake_sleep == 1)
{
  wake_sleep = 0; // set flag
  fprintf(Data,"\r\n Controller Awaken!!"); // To verify on hyperterminal
}
enable_interrupts(INT_EXT);
}

#INT_EXT1
void ext_isr1()
{
disable_interrupts(INT_EXT2); // Button pushed
if (wake_sleep == 1)
{
  wake_sleep = 0; // set flag
  fprintf(Data,"\r\n Controller Awaken!!"); // To verify on hyperterminal
}
enable_interrupts(INT_EXT2);
}



Subroutines Source Code: Functions.C


Code:


int1 A=0;
int  i;
int16  Inactivity=0;
float Y_2, X_2, Y_1, X_1,M_1,M_2,A_1,A_2,Cal0,Cal1,Cal2,Cal3;
char Dir_1='0', Dir_2='0';
float inc_1=0,inc_2=0;
Char JB1='0',JB2='0',PB1='0',PB2='0';
#define Button_1      PIN_B0
#define Button_2      PIN_B1
#define Joy_Button_1  PIN_C5
#define Joy_Button_2  PIN_C4
#define Tone          PIN_C3
// Buzzer
#define BUZZER_ON     output_low(PIN_C3)
#define BUZZER_OFF    output_High(PIN_C3)

// White LED
#define White_LED_OFF output_low(PIN_C2)
#define White_LED_ON  output_High(PIN_C2)
// Bi Color LED
#define Bi_Green_ON   output_high(PIN_C1); output_low(PIN_A5)
#define Bi_Red_ON     output_low(PIN_C1);  output_high(PIN_A5)
#define Bi_Led_OFF    output_high(PIN_C1); output_high(PIN_A5)
// Constants
#define Analog_factor 0.09765625
#define Joystick_Thres 9
#define Level         12

////////////////////////////////////////////////////////////////////////////////
void do_delay(int ms_delay, int num_ms, int us_delay, int num_us) // used for generating tones
{
 int i;
 for(i=0;i<num_ms;i++)
  delay_ms(250);
 delay_ms(ms_delay);
 for(i=0;i<num_us;i++)
  delay_us(250);
 delay_us(us_delay);
}
////////////////////////////////////////////////////////////////////////////////
void generate_tone(long frequency, long duration)   // used for generating tones
{
   int32 total_delay_time;                      // in microseconds
   long total_ms_delay_time, total_us_delay_time;
   int num_us_delays, num_ms_delays, ms_delay_time, us_delay_time;
   long num_periods;

   total_delay_time = (1000000/frequency)/2-10; // calculate total delay time (10 for error)

   total_ms_delay_time = total_delay_time/1000; // total delay time of ms
   num_ms_delays = total_ms_delay_time/250;     // number of 250ms delays needed
   ms_delay_time = total_ms_delay_time%250;     // left over ms delay time needed

   total_us_delay_time = total_delay_time%1000; // total delay time of us (ms already acounted for)
   num_us_delays = total_us_delay_time/250;     // number of 250us delays needed
   us_delay_time = total_us_delay_time%250;     // left over us delay time needed

   num_periods = ((int32)duration*1000)/(1000000/frequency);

   while((num_periods--) != 0)
   {
      do_delay(ms_delay_time, num_ms_delays, us_delay_time, num_us_delays);
      output_low(Tone);
      do_delay(ms_delay_time, num_ms_delays, us_delay_time, num_us_delays);
      output_high(Tone);
   }

   return;
}

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
Void Calibration()
{
////////////////  LEFT JOYSTICK # 2 ///////////////
Set_adc_channel(0);  // Point to analog port connected to Joystick 2 Y axis
delay_ms(1);
Cal0 = read_adc()*Analog_factor; // Read & Scale
delay_ms(1);

Set_adc_channel(1);;  // Point to analog port connected to Joystick 2 X axis
delay_ms(1);
Cal1 = read_adc()*Analog_factor;  // Read & Scale
delay_ms(1);

////////////////  RIGHT JOYSTICK # 1 //////////////
Set_adc_channel(2);  // Point to analog port connected to Joystick 1 Y axis
delay_ms(1);
Cal2 = read_adc()*Analog_factor;  // Read & Scale
delay_ms(1);

Set_adc_channel(3); // Point to analog port connected to Joystick 1 X axis
delay_ms(1);
Cal3 = read_adc()*Analog_factor; // Read & Scale
delay_ms(1);
//////////////////////////////////////////////////


If ((47.9 < Cal0) && (Cal0 < 48.1)) // within the expected range do nothing  use actual value "more accurate"
      {
      }
else
      {
        Cal0 = 48.0;  // Default Center value from testing will be used if the joystick has been touched during calibration or has a slight mechanical malfuction
      }
If ((48.7 < Cal1) && (Cal1 < 48.9))  // within the expected range do nothing use actual value "more accurate"
      {
      }
else
      {
        Cal1 = 48.8;  // Default Center value from testing will be used if the joystick has been touched during calibration or has a slight mechanical malfuction
      }

If ((48.7 < Cal2) && (Cal2 < 48.9))  // within the expected range do nothing use actual value "more accurate"
      {
      }
else
      {
        Cal2 = 48.8; // Default Center value from testing will be used if the joystick has been touched during calibration or has a slight mechanical malfuction
      }

If ((47.5 < Cal3) && (Cal3 < 47.7))  // within the expected range do nothing use actual value "more accurate"
      {
      }
else
      {
        Cal3 = 47.6; // Default Center value from testing will be used if the joystick has been touched during calibration or has a slight mechanical malfuction
      }
}
/////////////////////////////////////////////////////////////////////////////////
Void Read_Joystick()
{
////////////////  LEFT JOYSTICK # 2 //////////////////
Set_adc_channel(0);  // Select Analog port connected to joystick 2 Y axis
delay_ms(1);
Y_2 = ((read_adc()*Analog_factor)-Cal0); // Read,Scale & subtract center value
delay_ms(1);

Set_adc_channel(1); // Select Analog port connected to joystick 2 X axis
delay_ms(1);
X_2 = ((read_adc()*Analog_factor)-Cal1); // Read,Scale & subtract center value
delay_ms(1);
////////////////  RIGHT JOYSTICK # 1 ////////////////
Set_adc_channel(2); // Select Analog port connected to joystick 1 Y axis
delay_ms(1);
Y_1 = ((read_adc()*Analog_factor)-Cal2); // Read,Scale & subtract center value
delay_ms(1);

Set_adc_channel(3); // Select Analog port connected to joystick 1 X axis
delay_ms(1);
X_1 = ((read_adc()* Analog_factor)- Cal3);  // Read,Scale & subtract center value
delay_ms(1);
}

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

Void Trig_Calc() // Math Calculations "Cartesian to Polar Coordinates"
{
A_2     = (atan2(Y_2,X_2))*180/3.14;  // Find theta
M_2 = sqrt ((Y_2*Y_2) + (X_2*X_2));   // Find r magnitude
A_2= A_2+180;     // Adjust to 0 to 359 degrees
M_2= floor (M_2); // Floor r
A_2= Abs(A_2);

A_1     = (atan2(Y_1,X_1))*180/3.14; // Find theta
M_1 = sqrt ((Y_1*Y_1) + (X_1*X_1));  // Find r magnitude
A_1= A_1+180;     //// Adjust to 0 to 359 degrees
A_1= Abs(A_1);
M_1= floor (M_1); // Floor r
}

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

Void Actions() //Check if any buttons where pressed and where the joysticks where moved
{
if (!input(Joy_Button_1))        // if pressed
  {
   White_LED_ON;                 // while led on
   if(JB1 == '0')                // check previous condition "toggle"
     {
       JB1='1';                  // Toggle Flag & Status
       generate_tone (5000,200); // Audio conformation that the buttons was pressed
     }
   else
     {
       JB1='0';                  // Toggle Flag & Status
       generate_tone (2000,200); // Audio conformation that the buttons was pressed
     }
    White_LED_ON;                // while led on
    A = 1;                       // Activity
  }

if (!input(Joy_Button_2))        // if pressed
  {
   White_LED_ON;                 // while led on
   if(JB2 == '0')                // check previous condition "toggle"
     {
       JB2='1';                  // Toggle Flag & Status
       generate_tone (5000,200); // Audio conformation that the buttons was pressed
     }
   else
     {
       JB2='0';                  // Toggle Flag & Status
       generate_tone (2000,200); // Audio conformation that the buttons was pressed
     }
    A = 1;                       // Activity
  }
if (!input(Button_1))            // if pressed
  {
   White_LED_ON;                 // while led on
   if(PB1 == '0')                // check previous condition "toggle"
     {
       PB1='1';                  // Toggle Flag & Status
       generate_tone (5000,200); // Audio conformation that the buttons was pressed
     }
   else
     {
       PB1='0';                  // Toggle Flag & Status
       generate_tone (2000,200); // Audio conformation that the buttons was pressed
     }
    A = 1;                       // Activity
  }
if (!input(Button_2))            // if pressed
  {
   White_LED_ON;                 // while led on
   if(PB2 == '0')                // check previous condition "toggle"
     {
       PB2='1';                  // Toggle Flag & Status
       generate_tone (5000,200); // Audio conformation that the buttons was pressed
     }
   else
     {
       PB2='0';                  // Toggle Flag & Status
       generate_tone (2000,200); // Audio conformation that the buttons was pressed
     }
    A = 1;                       // Activity
  }



if (M_1 > Joystick_Thres) // if joystick 1 reading is greater than threhold value find which direction is pushed
   {
      A = 1;  // Activity
      // UP
      if((A_1 > 45)&&(A_1 < 135)) //A_1 Greater Than 45 & less Than 135
        {
          Bi_Green_ON; // green led on
          Dir_1='L';   // set direction to left
        }
      // LEFT
      if(((A_1 < 45)&&(A_1 >= 0))||((A_1 <= 360)&&(A_1 > 315))) //A_1 less Than 45 & Greater Than 135
        {
          Bi_Green_ON; // green led on
          Dir_1='U';   // set direction to up
        }
      // DOWN
      if((A_1 < 315)&&(A_1 > 225))//A_1 Less Than  315 & A_1 Greater Than 225
        {
          Bi_Green_ON; // green led on
          Dir_1='R';   // set direction to right
        }
      // RIGHT
      if((A_1 > 135 )&&(A_1 < 225))//A_1 Greater Than  135 & A_1 Less Than 225
        {
          Bi_Green_ON; // green led on
          Dir_1='D';   // set direction to down
        }
       inc_1 = floor(M_1); // adjust the magnitude reading
       if (inc_1 > 50)     // 50 will be set as the max reading from the center. The range is (Threshold + 1 to 50)
       {
          inc_1=50;
       }
   }


if (M_2 > Joystick_Thres) // if joystick 2 reading is greater than threhold value find which direction is pushed
   {
      A = 1;
      // UP
      if((A_2 > 45)&&(A_2 < 135)) //A_1 Greater Than 45 & less Than 135
        {
           Bi_Green_ON;  // green led on
           Dir_2='L';    // set direction to left
        }
      // LEFT
      if(((A_2 < 45)&&(A_2 >= 0))||((A_2 <= 360)&&(A_2 > 315))) //A_1 less Than 45 & Greater Than 135
        {
           Bi_Green_ON;  // green led on
           Dir_2='U';    // set direction to up
        }
      // DOWN
      if((A_2 < 315)&&(A_2 > 225))//A_1 Less Than  315 & A_1 Greater Than 225
        {
           Bi_Green_ON; // green led on
           Dir_2='R';   // set direction to right
        }
      // RIGHT
      if((A_2 > 135 )&&(A_2 < 225))//A_1 Greater Than  135 & A_1 Less Than 225
        {
           Bi_Green_ON; // green led on
           Dir_2='D';   // set direction to down
        }
       inc_2 = floor(M_2); // adjust the magnitude reading
       if (inc_2 > 50)  // 50 will be set as the max reading from the center. The range is (Threshold + 1 to 50)
       {
          inc_2=50;
       }
   }
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
Void Transmit() // Transmit data if activity flag A == 1
{
  if (A == 1)
  {
       output_high(Pin_C1);//RTS
       delay_ms(1); //delay
       fprintf(Data," +$$MOVE:%c,%2.0f,%c,%2.0f,%C,%C,%C,%C*\r",Dir_1,inc_1,Dir_2,inc_2,JB1,JB2,PB1,PB2); // Transmit Data frame to Xbee module
       delay_us(1); //delay
       output_low(Pin_C1);//RTS
       Dir_1 = 'X'; // clear variable
       Dir_2 = 'X'; // clear variable
       inc_1 =  0 ; // clear variable
       inc_2 =  0 ; // clear variable
       A = 0;       // clear flag "actions flag"
       White_LED_OFF; // white leed off if it was on
       Bi_Led_OFF;    // led off
       BUZZER_OFF;    // buzzer off
       Inactivity = 0; // clear inactivity counter
       delay_us(1); // delay
  }
}




Test it out with an Xbee module connected to Hyper Terminal.


Kerron Manwaring
[/url]


Last edited by Kerron on Thu Jan 16, 2014 12:40 pm; edited 4 times in total
Cromfel



Joined: 25 Aug 2011
Posts: 1

View user's profile Send private message

PostPosted: Thu Aug 25, 2011 3:21 am     Reply with quote

Very inspiring work mate! I will try to implement something along these lines Smile
Raulsoft



Joined: 28 Jan 2010
Posts: 6

View user's profile Send private message

File code
PostPosted: Tue Aug 04, 2015 7:31 am     Reply with quote

How do I download the source code?

In the URL above.

thanks
Ttelmah



Joined: 11 Mar 2010
Posts: 19535

View user's profile Send private message

PostPosted: Tue Aug 04, 2015 8:01 am     Reply with quote

Just cut and paste....
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group