Go to Post One career isn't really more prestigious than another; not in and of itself. More than anything it depends on what you do with it. - Matt Leese [more]
Home
Go Back   Chief Delphi > Technical > Programming > Java
CD-Media   CD-Spy  
portal register members calendar search Today's Posts Mark Forums Read FAQ rules

 
Reply
Thread Tools Rate Thread Display Modes
  #1   Spotlight this post!  
Unread 24-02-2011, 19:49
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
my code

Here is a copy of my code.
Please comment on it! I know i could just do the import "*" thing but i chose not to for future references!
If you have any questions please ask, but otherwise my comments in the code seem pretty straight forward!
Code:
  /*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

package edu.wpi.first.wpilibj.templates;


import edu.wpi.first.wpilibj.SimpleRobot;//imports the robot template.
import edu.wpi.first.wpilibj.camera.AxisCamera;//imports camera.
import edu.wpi.first.wpilibj.Joystick;//imports the controller.
import edu.wpi.first.wpilibj.Timer;//imports the timer.
import edu.wpi.first.wpilibj.RobotDrive;//imports the drive.
import edu.wpi.first.wpilibj.Watchdog;//imports the safety timer.
import edu.wpi.first.wpilibj.Jaguar;//imports the arm jaguar.
import edu.wpi.first.wpilibj.DoubleSolenoid;//imporst the solenoids used on the robot.
import edu.wpi.first.wpilibj.Compressor;//imports compressor.
/**
 * The VM is configured to automatically run this class, and to call the
 * functions corresponding to each mode, as described in the SimpleRobot
 * documentation. If you change the name of this class or the package after
 * creating this project, you must also update the manifest file in the resource
 * directory.
 */
public class RobotTemplate extends SimpleRobot {
   RobotDrive drive = new RobotDrive(1,2); // drives the robot around; no jaguars needed to control unless specified.
   RobotDrive Arm = new RobotDrive(3,0);
   Joystick leftStick = new Joystick(1); // this is our game controller: can be anything that has a joystick.
   Timer timer = new Timer(); // Just a timer.
   Watchdog Fluffy = Watchdog.getInstance(); // safety timer.
   AxisCamera Cam = AxisCamera.getInstance(); // shows image on dashboard.
   Compressor comp = new Compressor(1,1); // compressor is pluged in digital I/O port 1, analog port 1.
   DoubleSolenoid grab = new DoubleSolenoid(1,2); // this is the solenoid that controls our manipulator.
   DoubleSolenoid deployM = new DoubleSolenoid(3,4);//This is the solenoid that controls our deploment.
   Jaguar arm = new Jaguar(3); // speed controller that controlls our arm hence the "arm" for the name.
   
           

    /**
     * This function is called once each time the robot enters autonomous mode.
     */
    public void autonomous() {
       while(isAutonomous()) {
       comp.start();
           {
       timer.start(); // Timer starts
       while(timer.get() < 4) { //while the timer is less than 3 sec. the arm will turn.
           Arm.drive(.75,0);//arm moves towards peg from starting configuration.
           timer.stop(); // stops the timer after 3 seconds.
       }
       Arm.drive(0,0);//Stops the arm so it doesnt kill anyone
           {
           timer.reset();// the timer resets it self.
           timer.start();//the timer starts again.
           while(timer.get()<4) {//while the timer is less than 4 sec. the robot will drive.
               drive.drive(.3,0);  // robot drives forward at 30% speed and 0% turn.
               timer.stop();//stops the arm from moving after the 4 seconds is up
               }
           drive.drive(0,0);//stops the robot so it doesn't kill anyone.
               {
                   grab.set(DoubleSolenoid.Value.kForward);// solenoid for the manipulator extends until let go.
               }
               {drive.drive(-.15,0);//drives backwards at 15% speed, zero turn so that we can get closer to our feeder station
    }
   }
  }
  comp.stop();
 }
}
    

    /**
     * This function is called once each time the robot enters operator control.
     */
    public void operatorControl() 
    {
        comp.start(); // our compressor starts.
        AxisCamera.getInstance();// the axis camera shows in dashboard not sure if we need this .
        Fluffy.setExpiration(5.0); // safety timer is set to call every 5 seconds.
        while  (isOperatorControl  () && isEnabled())// operator control is enabled.
        {
            Fluffy.feed();// feed safety timer.
            drive.arcadeDrive(leftStick);//Drive the robot with the left thumbstick on the game controller or the left joystick if using two.
            Fluffy.feed();// feed safety timer.
                if (leftStick.getRawButton(5))
                {
                    arm.set(.5);// when we press button 5 our arm goes up at 50% speed.
                }
                else if (leftStick.getRawButton(6))
                {
                    arm.set(-.5);//arm goes toward the ground at 50% speed when we press button 6. though it doesnt seem to do it.
                }
                else
                {
                    arm.set(0);// arm doesnt do anything when we dont press a button.
                if(leftStick.getRawButton(7))
                {
                    arm.set(1);// arm goes away from the ground at 100% speed when we press button 7.
                    }
                else if (leftStick.getRawButton(8))
                {
                    arm.set(-1);// arm goes towards the ground at 100% speed when we press button 8.
                }
                else
                {
                        arm.set(0);// arm doesnt do anything
                if(leftStick.getRawButton(2))
                {
                    Fluffy.feed();// feed safety timer.
                    grab.set(DoubleSolenoid.Value.kForward);// solenoid for the manipulator extends until let go.
                    Fluffy.feed();// feed safety timer.
                    }
                else
                {
                     Fluffy.feed();// feed safety timer.
                     grab.set(DoubleSolenoid.Value.kReverse);// solenoid for the manipulator retracts.
                     Fluffy.feed();// feed safety timer.
                if (leftStick.getRawButton(10))
                {
                    Fluffy.feed();// feed safety timer.
                    deployM.set(DoubleSolenoid.Value.kForward);
                    Fluffy.feed();// feed safety timer.
                    }
                else
                {
                     Fluffy.feed();//feed safety timer.
                     deployM.set(DoubleSolenoid.Value.kReverse);
                     Fluffy.feed();//feed safety timer.
                }
      }
     Timer.delay(.005);// call to check for a button being pressed every 5 milliseconds!
     }
    comp.stop();// compressor stops.
   }
  }
 }
}

Last edited by Garten Haeska : 24-02-2011 at 21:30. Reason: Updated the code
Reply With Quote
  #2   Spotlight this post!  
Unread 24-02-2011, 19:56
Robby Unruh's Avatar
Robby Unruh Robby Unruh is offline
*insert random dial-up tone here*
FRC #3266 (Robots R Us)
Team Role: Coach
 
Join Date: Feb 2010
Rookie Year: 2010
Location: Eaton, OH
Posts: 338
Robby Unruh will become famous soon enough
Re: my code

From first glance I see nothing absolutely deadly in your code. But watch out with your Fluffy.setExpiration(5.0);
5 seconds may not seem like much, but when you're on the field that's like an hour!

Also, '[code]' is fun.
__________________
[Robots R Us #3266]
2015: Georgia Southern Classic (Winners / Thanks 1319 & 1648!), Queen City
2014: Crossroads, Queen City
2013: Buckeye, Queen City, Crossroads
2012: Buckeye, Queen City

2011: Buckeye
2010: Buckeye
Reply With Quote
  #3   Spotlight this post!  
Unread 24-02-2011, 21:27
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
Re: my code

Quote:
Originally Posted by Robby Unruh View Post
From first glance I see nothing absolutely deadly in your code. But watch out with your Fluffy.setExpiration(5.0);
5 seconds may not seem like much, but when you're on the field that's like an hour!

Also, '[code]' is fun.
haha thanks for telling about the the '[code]' thing it looks alot better now!
__________________
"Sometimes the questions are

complicated but the answers are simple."

- Dr. Seuss
Reply With Quote
  #4   Spotlight this post!  
Unread 25-02-2011, 22:56
kinganu123 kinganu123 is offline
Registered User
FRC #1747
Team Role: Mentor
 
Join Date: Jan 2010
Rookie Year: 2008
Location: Piscataway, NJ
Posts: 243
kinganu123 is on a distinguished road
Re: my code

WOAH!!! DOUBLE SOLENOIDS!!! ALL THE WAY (you guys get the rest)
__________________
Reply With Quote
  #5   Spotlight this post!  
Unread 26-02-2011, 00:13
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
Re: my code

Quote:
Originally Posted by kinganu123 View Post
WOAH!!! DOUBLE SOLENOIDS!!! ALL THE WAY (you guys get the rest)
im not sure i do get the rest?
__________________
"Sometimes the questions are

complicated but the answers are simple."

- Dr. Seuss
Reply With Quote
  #6   Spotlight this post!  
Unread 26-02-2011, 16:19
Patrickwhite's Avatar
Patrickwhite Patrickwhite is offline
May the North be with you
FRC #0610 (The Coyotes)
Team Role: Programmer
 
Join Date: Dec 2008
Rookie Year: 2008
Location: Toronto
Posts: 88
Patrickwhite is a glorious beacon of lightPatrickwhite is a glorious beacon of lightPatrickwhite is a glorious beacon of lightPatrickwhite is a glorious beacon of lightPatrickwhite is a glorious beacon of lightPatrickwhite is a glorious beacon of light
Re: my code

It's a 'double rainbow' joke. I did get a kick out of 'fluffy' but agree that five seconds is enough for your robot to do serious damage to itself and things around it.
You do feed him a LOT though... Really, you only need to feed him once per loop. In your teleop loop there, you feed him six times every loop! Hope you take him for lots of walks!

Erm. Bad jokes aside, it looks good - very well commented especially. Few questions though:
Does the dead reckoning [i.e. based solely on time] autonomous work consistently (if you've tested it)? From past experience I find that dead reckoning is subject to even the slightest mechanical or electrical change.
Have there been any problems with having the arm RobotDrive try to access channel 0? If it's only one speed controller, and it's not quite a 'drive', why are you using RobotDrive instead of just making a Jaguar?

Also -
Quote:
I know i could just do the import "*" thing but i chose not to for future references!
There are lots of arguments for NOT importing everything in the package - like a slower compile time (especially considering WPIlib builds pretty slowly in the first place), easily seeing what your code is made of, etc. So good job choosing not to!
__________________
while(!going.isTough());
tough.exit();

What will we do tonight, Warfa?
The same thing we do every night, Patrick. Sit and wait for Electrical.
Reply With Quote
  #7   Spotlight this post!  
Unread 27-02-2011, 16:57
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
Re: my code

well the arm is two window motors running in snyc on port 3 because im using a split pwm cable. I dont really know how to program it. I have not tested the autonomus because me team doesnt realize that a programmer should get some time with the robot, and could you give me any help on how to run my arm, on a joystick, im using a game controller with two joysticks, the left is for arcade drive and the right i want to be bor the arm. and i do have my arm declared as a jaguar in a spot too.

Code:
/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2008. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

package edu.wpi.first.wpilibj.templates;


import edu.wpi.first.wpilibj.SimpleRobot;
//imports the robot template.
import edu.wpi.first.wpilibj.camera.AxisCamera;
//imports camera.
import edu.wpi.first.wpilibj.Joystick;
//imports the controller.
import edu.wpi.first.wpilibj.Timer;
//imports the timer.
import edu.wpi.first.wpilibj.RobotDrive;
//imports the drive.
import edu.wpi.first.wpilibj.Watchdog;
//imports the safety timer.
import edu.wpi.first.wpilibj.Jaguar;
//imports the arm jaguar.
import edu.wpi.first.wpilibj.DoubleSolenoid;
//imporst the solenoids used on the robot.
import edu.wpi.first.wpilibj.Compressor;
//imports compressor.

/**
 * The VM is configured to automatically run this class, and to call the
 * functions corresponding to each mode, as described in the SimpleRobot
 * documentation. If you change the name of this class or the package after
 * creating this project, you must also update the manifest file in the resource
 * directory.
 */
public class RobotTemplate extends SimpleRobot {
   RobotDrive drive = new RobotDrive(1,2);
   // drives the robot around; no jaguars needed to control unless specified.
   RobotDrive Arm = new RobotDrive(3,3);
   //drives the arm in autonomy.
   Joystick leftStick = new Joystick(1);
   // this is our game controller: can be anything that has a joystick.
   Joystick rightStick = new Joystick(2);
   // kellers joystick for lights.
   Timer timer = new Timer();
   // Just a timer.
   Watchdog Fluffy = Watchdog.getInstance();
   // safety timer.
   AxisCamera Cam = AxisCamera.getInstance();
   // shows image on dashboard.
   Compressor comp = new Compressor(1,1);
   // compressor is pluged in digital I/O port 1, analog port 1.
   DoubleSolenoid grab = new DoubleSolenoid(1,2);
   // this is the solenoid that controls our manipulator.
   DoubleSolenoid deployM = new DoubleSolenoid(3,4);
   //This is the solenoid that controls our deploment.
   Jaguar arm = new Jaguar(3);
   // speed controller that controlls our arm hence the "arm" for the name.

   /**
     * This function is called once each time the robot enters autonomous mode.
     */
    public void autonomous() {
       while(isAutonomous()) {
       comp.start();
           {
       timer.start();
       // Timer starts
       while(timer.get() < 3) {
           //while the timer is less than 3 sec. the arm will turn.
           Arm.drive(1,0);
           //arm moves towards peg from starting configuration
           //at 100% speed, zero % turn because it cant turn!
           timer.stop();
           // stops the timer after 3 seconds.
       }
       Arm.drive(0,0);
       //Stops the arm so it doesnt kill anyone
           {
           timer.reset();
           // the timer resets it self.
           timer.start();
           //the timer starts again.
           while(timer.get()<4) {
               //while the timer is less than 4 sec. the robot will drive.
               drive.drive(.3,0);
               // robot drives forward at 30% speed and 0% turn.
               timer.stop();
               //stops the robot from moving after the 4 seconds is up.
               }
           drive.drive(0,0);
           //stops the robot so it doesn't kill anyone.
               {
                   grab.set(DoubleSolenoid.Value.kForward);
                   // solenoid for the manipulator extends until let go.
               }
               {
               timer.reset();
               // the timer resets itself.
               timer.start();
               //the timer restarts itself again.
               while(timer.get()<6)
                   //while the timer is less than 6 seconds the 
                   //robot will go in reverse
                     drive.drive(-.15,0);
                     //drives backwards at 15% speed, zero turn so that
                     //we can get closer to our feeder station.
                     timer.stop();
                //Stops the robot from moving after the 6 seconds are up.
               }
               drive.drive(0,0);
   }
  }
  comp.stop();
  // compressor stops not sure if we need this.
 }
}
    

    /**
     * This function is called once each time the robot enters operator control.
     */
    public void operatorControl() 
    {
        comp.start();
        // our compressor starts.
        AxisCamera.getInstance();
        // the axis camera shows in dashboard not sure if we need this .
        Fluffy.setExpiration(5.0);
        // safety timer is set to call every 5 seconds.
        while  (isOperatorControl  () && isEnabled())
            // operator control is enabled.
        {
            Fluffy.feed();
            // feed safety timer.
            drive.arcadeDrive(leftStick);
            //Drive the robot with the left thumbstick on the game controller or the left joystick if using two.
            Fluffy.feed();
            // feed safety timer.
            Arm.tankDrive(leftStick, 0, leftStick, 4);
                if (leftStick.getRawButton(5))
                {
                    arm.set(.5);
                    // when we press button 5 our arm goes up at 50% speed.
                }
                else if (leftStick.getRawButton(6))
                {
                    arm.set(-.5);
                    //arm goes toward the ground at 50% speed when we press button 6. though it doesnt seem to do it.
                }
                else
                {
                    arm.set(0);
                    // arm doesnt do anything when we dont press a button.
                if(leftStick.getRawButton(7))
                {
                    arm.set(1);
                    // arm goes away from the ground at 100% speed when we press button 7.
                    }
                else if (leftStick.getRawButton(8))
                {
                    arm.set(-1);
                    // arm goes towards the ground at 100% speed when we press button 8.
                }
                else
                {
                        arm.set(0);
                        // arm doesnt do anything.
                if(leftStick.getRawButton(2))
                {
                    Fluffy.feed();
                    // feed safety timer.
                    grab.set(DoubleSolenoid.Value.kForward);
                    // solenoid for the manipulator extends until let go.
                    Fluffy.feed();
                    // feed safety timer.
                    }
                else
                {
                     Fluffy.feed();
                     // feed safety timer.
                     grab.set(DoubleSolenoid.Value.kReverse);
                     // solenoid for the manipulator retracts.
                     Fluffy.feed();
                     // feed safety timer.
                if (leftStick.getRawButton(10))
                {
                    Fluffy.feed();
                    // feed safety timer.
                    deployM.set(DoubleSolenoid.Value.kForward);
                    //the minibot deployment is launched make sure you dont let go!
                    Fluffy.feed();
                    // feed safety timer.
                    }
                else
                {
                     Fluffy.feed();
                     //feed safety timer.
                     deployM.set(DoubleSolenoid.Value.kReverse);
                     //minibot deployment is retracted make sure minibot is climbing first!
                     Fluffy.feed();
                     //feed safety timer.
                
        
       }
      }
     Timer.delay(.005);
     // call to check for a button being pressed every 5 milliseconds!
     }
    comp.stop();
    // compressor stops.
   }
  }
 }
}
__________________
"Sometimes the questions are

complicated but the answers are simple."

- Dr. Seuss

Last edited by Garten Haeska : 27-02-2011 at 20:28. Reason: put in my new code
Reply With Quote
  #8   Spotlight this post!  
Unread 27-02-2011, 22:08
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
Exclamation Re: my code

i do need help though i want to stop my arm when it hits a limit switch, now ive searched CD and cant quite figure it out, but when it is triggered i want my arm to stop and when it is not triggered i want my arm to drive. How might i go about doing this. btw. this would replace the timer reference in my autonomy. Please help!!!!
__________________
"Sometimes the questions are

complicated but the answers are simple."

- Dr. Seuss
Reply With Quote
  #9   Spotlight this post!  
Unread 28-02-2011, 09:20
Robby Unruh's Avatar
Robby Unruh Robby Unruh is offline
*insert random dial-up tone here*
FRC #3266 (Robots R Us)
Team Role: Coach
 
Join Date: Feb 2010
Rookie Year: 2010
Location: Eaton, OH
Posts: 338
Robby Unruh will become famous soon enough
Re: my code

Quote:
Originally Posted by Garten Haeska View Post
i do need help though i want to stop my arm when it hits a limit switch, now ive searched CD and cant quite figure it out, but when it is triggered i want my arm to stop and when it is not triggered i want my arm to drive. How might i go about doing this. btw. this would replace the timer reference in my autonomy. Please help!!!!
I'm not 100% on what you mean, but if you want your arm to move only while the trigger is being held, you'd have to use a conditional, an essential in any programming language.

Code:
public void operatorControl() {
    if(joystick.getTrigger()) {

        // move the arm based on the joystick's Y-axis
        arm.set(joystick.getY());

    } else {

        /* stop the arm if the trigger isn't held down
         * useful note: the arm will remain in it's position
         * until you move it again. this simply stops the arm
         * from moving, not sets it back down in
         * it's starting position. you can code this easily.
         */
        arm.set(0.0);

    }

    /* just for reference, you would still be able
        to drive if the trigger is not held down. */
    robotDrive.drive(joystick);

}
__________________
[Robots R Us #3266]
2015: Georgia Southern Classic (Winners / Thanks 1319 & 1648!), Queen City
2014: Crossroads, Queen City
2013: Buckeye, Queen City, Crossroads
2012: Buckeye, Queen City

2011: Buckeye
2010: Buckeye
Reply With Quote
  #10   Spotlight this post!  
Unread 28-02-2011, 09:25
mwtidd's Avatar
mwtidd mwtidd is offline
Registered User
AKA: mike
FRC #0319 (Big Bad Bob)
Team Role: Mentor
 
Join Date: Feb 2005
Rookie Year: 2003
Location: Boston, MA
Posts: 714
mwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond reputemwtidd has a reputation beyond repute
Re: my code

Quote:
Originally Posted by Garten Haeska View Post
i do need help though i want to stop my arm when it hits a limit switch, now ive searched CD and cant quite figure it out, but when it is triggered i want my arm to stop and when it is not triggered i want my arm to drive. How might i go about doing this. btw. this would replace the timer reference in my autonomy. Please help!!!!
beware of the logic here, as if the limit switch is triggered there is no way to get the arm off of the switch

my solution to this is to rather than stop, prohibit movement in one direction.

pseudo code would look something like this.

if(limit == true && y>1)
stop();
else
arm.run(js.getY);
__________________
"Never let your schooling interfere with your education" -Mark Twain
Reply With Quote
  #11   Spotlight this post!  
Unread 28-02-2011, 21:31
Garten Haeska's Avatar
Garten Haeska Garten Haeska is offline
Registered User
AKA: Garty
FRC #2861 (Infinity's End)
Team Role: Mentor
 
Join Date: Jan 2011
Rookie Year: 2009
Location: Lake City, MN
Posts: 219
Garten Haeska has a spectacular aura aboutGarten Haeska has a spectacular aura about
Re: my code

Quote:
Originally Posted by lineskier View Post
beware of the logic here, as if the limit switch is triggered there is no way to get the arm off of the switch

my solution to this is to rather than stop, prohibit movement in one direction.

pseudo code would look something like this.

if(limit == true && y>1)
stop();
else
arm.run(js.getY);
This is somewhat what i want to do, i only want the switch to be powered in auto. so then i can extend past it to floor load, and i just want the switch to turn off the arm when the arm hits the switch. im just clueless right now.
__________________
"Sometimes the questions are

complicated but the answers are simple."

- Dr. Seuss
Reply With Quote
Reply


Thread Tools
Display Modes Rate This Thread
Rate This Thread:

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump


All times are GMT -5. The time now is 12:35.

The Chief Delphi Forums are sponsored by Innovation First International, Inc.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.
Copyright © Chief Delphi