The touch sensor

We use the touch sensor to learn how LeJOS handles sensors. Our aim is to set up the sensor and detect when the touch sensor is pressed. Once again, the first step is to set up the project structure with the root folder named Touch:

Touch
    | ---- src
              | ---- Touch.java
       | ---- TouchSensor.java
    | ---- build.gradle
    | ---- ev3
       | ---- DBusJava
       | ---- ev3classes

The build.gradle file is set up as usual with the main class named Touch after both the root folder name and the Touch class (inside the Touch.java file), which contains the main() function where the execution starts.

The TouchSensor.java file

LeJOS treats EV3 sensors in a consistent and generalized fashion. This means that the class for each EV3 sensor implements a fetchSample() method to get measurements from the sensor. The downside of this generalization is that the EV3TouchSensor class does not have an isPressed() method as one would expect, that is, there is no straightforward way of determining whether the touch sensor is being pressed or not.

This is a conscious decision by the designers of LeJOS, since they intend it for serious programming. Since our aim is to gain proficiency in LeJOS, this presents a good opportunity to learn how to enhance LeJOS classes to meet our needs.

To that end, we construct a TouchSensor class that extends the built-in EV3TouchSensor class to add an isPressed() method to it, which when called lets us know whether the touch sensor is currently pressed or not. We begin by presenting the entire contents of the TouchSensor.java class in the following code snippet (a detailed explanation will follow):

import lejos.hardware.port.Port;
import lejos.hardware.sensor.EV3TouchSensor;

public class TouchSensor extends EV3TouchSensor
{
    public TouchSensor(Port port)
    {
        super(port);
    }

    public boolean isPressed()
    {
        float[] sample = new float[1];
        fetchSample(sample, 0);

        return sample[0] != 0;
    }
}

While trying to understand this code, it is helpful to read the EV3TouchSensor.java class along with it (which resides in the ev3/ev3classes/src/lejos/hardware/sensor/ folder and whose documentation can be found at http://www.lejos.org/ev3/docs/). We are basically enhancing the EV3TouchSensor class by adding only the isPressed() method to it.

The first two lines of the code import the Port and EV3TouchSensor classes that are used in the rest of the code. We then declare the TouchSensor class:

public class TouchSensor extends EV3TouchSensor
{

The key feature of this definition is the keyword extends. This declares that the TouchSensor class enhances the EV3TouchSensor class. This phenomenon is known as inheritance. The TouchSensor class is the child of the EV3TouchSensor class and inherits all of the members (variables) and methods (functions) of its parent. This methodology is known as object-oriented programming and is an extremely powerful and almost essential programming paradigm, particularly in Java.

The first thing we do in the TouchSensor class is declare its constructor. The constructor is a special method in a class which is called whenever a new instance of the class is created, that is, whenever we create a new object of the TouchSensor class:

public TouchSensor(Port port)
  {
    super(port);
  }

The constructor is given the same name as the class (TouchSensor). In Java, any class that extends a class with explicit constructors must explicitly declare its own constructors. The EV3TouchSensor class has two constructors that use a Port and AnalogPort variables respectively. Since we are only interested in touch sensors connected to a sensor port, we only define the constructor that has a Port variable as its argument.

The constructor consists of only one statement: super(port). This statement is a special method that tells Java to run the corresponding constructor of the parent class (EV3TouchSensor). This is a rather standard tactic, defining the required explicit constructor and telling it to run the parent's constructor. This takes care of the initialization that is required to set up the sensor.

The TouchSensor class defines only one additional method:

public boolean isPressed()
{
  float[] sample = new float[1];
  fetchSample(sample, 0);

  return sample[0] != 0;
}

The purpose of this method is to return a Boolean that indicates whether the touch sensor is currently pressed (true) or not (false). The first line declares the method to be publicly accessible, takes no arguments, and returns a Boolean value.

The first statement in the method float[] sample = new float[1]; declares an array of float variables of length 1. We use this array in the second statement fetchSample(sample, 0);. The fetchSample() method is defined in the EV3TouchSensor class. It accepts an array of floats and an index value (0 in this case). The method measures the state of the touch sensor and stores the result in the specified index of the array. We pass in the array sample (of length 1) and the index value 0 (the only one allowed since the array has length 1 and so contains only one element with index 0). The method fetchSample takes these inputs and stores the measured value in sample[0]. Looking at the definition of the fetchSample method in the EV3TouchSensor class will be helpful.

The final statement of the isPressed method returns the Boolean value calculated by the method: return sample[0] != 0;. The expression sample[0] != 0 compares the measured value sample[0] to the constant value 0 and evaluates to a Boolean that is returned by the method.

The way the EV3TouchSensor class is defined, a value of 1 means that the button on the sensor is pressed and 0 means that it is not. So, the expression sample[0] != 0 returns true when the measured value is not 0, which means the button is currently pressed. The purpose of this class will become clearer in the next section when we use it to program EV3 to detect a press on the touch sensor.

The Touch.java file

The Touch.java file contains the main class of the project, in particular, the main method where execution starts. It uses the TouchSensor class we defined earlier to determine the state (pressed or not) of the touch sensor:

import lejos.hardware.port.SensorPort;
import lejos.utility.Delay;

public class Touch
{
    public static void main(String[] args)
    {
        log("Program Starting");

        TouchSensor uTouch = new TouchSensor(SensorPort.S1);
        waitForTouch(uTouch);

        log("Program Ending");
    }
…

    private static void waitForTouch(TouchSensor uTouch)
    {
       log("Waiting for press on Touch Sensor");

        while (! uTouch.isPressed())
        {
            Delay.msDelay(100);
        }

        log("Touch Sensor pressed.");
    }


    private static void log(String msg)
    {
        System.out.println("log>	" + msg);
    }
}

Explanation

The first two lines, as usual, import the LeJOS classes that are used by the Touch class, namely SensorPort and Delay. Since the TouchSensor.java file is in the same folder as Touch.java, the TouchSensor class can be used without having to import it explicitly.

The Touch class defines two methods in addition to main. We are already familiar with the log method, which is used to print messages to the terminal. The waitForTouch method accepts a TouchSensor object as its argument and refuses to return a value until the touch sensor is pressed:

private static void waitForTouch(TouchSensor uTouch)
  {
      log("Waiting for press on Touch Sensor");

      while (! uTouch.isPressed())
      {
         Delay.msDelay(100);
      }

      log("Touch Sensor pressed.");
  }

The first statement in this method is a log that tells us that the function is about to start waiting for a press to occur. The next line initiates a while loop that repeats until the condition ! uTouch.isPressed() becomes true. The ! symbol inverts the Boolean value of the expression that follows, so basically, we instruct the while loop to keep looping until uTouch.isPressed() returns true, which when inverted by ! makes the while condition false and the execution exits the loop.

The single statement inside the while loop is Delay.msDelay(100);. This uses the LeJOS Delay class to pause execution for 100 milliseconds. After the delay, we arrive at the start of the while loop and test the condition again, that is, poll the touch sensor using uTouch.isPressed(), asking it whether it is currently pressed. This is the isPressed function we wrote ourselves in the TouchSensor.java file. uTouch is the object of the TouchSensor class we created in the main function and passed into the current waitForTouch function as an argument (discussed in the next paragraph). The uTouch.isPressed() method means we are running the isPressed() method associated with the specific object uTouch.

The setup here is a classic robotics paradigm where a sensor is polled (queried) for its state repeatedly with a small delay built in so that we don't overwhelm it. The delay of 100 milliseconds means that the sensor is polled 10 times a second, a sufficiently frequent sampling to detect a press occurrence.

The uTouch.isPressed() method returns true only if the touch sensor is currently being pressed. If that is the case, then it causes the while loop to terminate. The execution shifts to the first line after the while loop, which is the log statement informing us that the sensor has been pressed. After printing the log, we reach the end of the function and therefore return from it.

Until such time as the touch sensor is pressed, the method uTouch.isPressed() evaluates to false and so execution remains stuck inside the loop. This is a simple and elegant technique of waiting for the sensor to be touched; keep asking the sensor until such time that it responds in the affirmative.

The log and waitForTouch methods are of course used in the main function, which is where execution starts:

public static void main(String[] args)
{
    log("Program Starting");

    TouchSensor uTouch = new TouchSensor(SensorPort.S1);
    waitForTouch(uTouch);

    log("Program Ending");
}

The first statement logs that the program is starting. The next statement is crucial:

TouchSensor uTouch = new TouchSensor(SensorPort.S1);

The preceding line of code creates a new object of the TouchSensor class and stores it in the variable uTouch. The argument passed into the constructor is SensorPort.S1, which declares that this object corresponds to a touch sensor that is connected to sensor port 1 on EV3.

The uTouch object is then passed to the waitForTouch method in the next statement. This causes execution to remain in this function until the touch sensor is pressed. When that happens, execution can proceed to the next log statement. This informs us that the program has reached its end, following which the program ends.

Compile, deploy, and execute

Compilation, deployment, and execution is performed exactly as in the case of the motor project. This is in fact the purpose of structuring the projects and their build.gradle files in an analogous fashion so that the compile-deploy-execute cycle is identical.

Upon successful execution, you will be able to monitor the logs (printed to the terminal) and observe EV3 detect the touch sensor being pressed and respond by terminating the program.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.17.91