Aegidius
 Plüss   Aplulogo
     
 www.aplu.ch      Print Text 
© 2021, V10.4
 
  NxtLib
 
 


Third example: Temperature Sensor (NxtJLibMP for Mobile Phones)

Purpose: Extend the list of sensors with a new sensor type that represents a temperature probe plugged into port S1.

Here again we realize the strength of the object-oriented programming: A newly added sensor device is seamlessly integrated into the existing class hierarchy. It is just another class derived from Sensor and becomes automatically a Part, that may be added to an NxtRobot with addPart(). Because it overrides init() and cleanup(), due to the polymorphy these methods are automatically called from the underlying system.

For the sake of simplicity we omit the implementation of the event model and declare only one additional method getDegrees() that performs a temperature measurement and returns the calibrated value in degrees (celsius).

Thus the class diagram is extended with the class TemperatureSensor:

 

   

 

The implementation is straightforward. The constructor takes a SensorPort reference and initializes with super() the base class Sensor.

// TemperatureSensor.java

import
 ch.aplu.nxt.*;

public
 class TemperatureSensor extends Sensor
{
  
private final int zeroPoint = 613;
  
private final double scaleFactor = -0.25;
  
  
public TemperatureSensor(SensorPort port)
  
{
    
super(port);
  
}
  
  
protected void init()
  
{
   
setTypeAndMode(TEMPERATURE, RAWMODE);
  
}
  
  
protected void cleanup()
  
{}
    
  
public double getDegrees()
  
{
    
int value = readRawValue();
    
return scaleFactor * (value - zeroPoint);
  
}
}

(The calibration values zeroPoint and scaleFactor should be adapted to your temperature probe and your temperature unit preferences.)

The application runs as a MIDlet on a Java-enabled mobile phone. It performs a temperature measurement every 2 seconds and display the reported value in a console window. When the temperature exceeds a given level, a warming sound is emitted.

// TempSensorGidlet.java

import
 ch.aplu.gidlet.*;
import
 ch.aplu.nxt.*;

public
 class TempSensorGidlet extends Gidlet
{
  
private final int securityLevel = 25;  // Degrees
  
private final int delayTime = 2000;  // ms
  
private NxtRobot robot;
  
private volatile boolean isRunning = true;

  
public void main()
 
{
    robot 
= new NxtRobot(nullfalse);
    
if (!robot.connect())
      
return;
    MConsole c 
= new MConsole(0, WHITE, BLACK, 2);
    c.
println("Remote Temperature");
    TemperatureSensor ts 
= new TemperatureSensor(SensorPort.S1);
    robot.
addPart(ts);
    
int time = 0;
    
while (isRunning)
    
{
      
double value = ts.getDegrees();
      c.
println("At " + time/1000 + " s: " + value + " deg.");
      
if (value > securityLevel)
      
{
        c.
println("  Temperature too high");
        
beep(2);
      
}
      Tools.
delay(delayTime);
      time 
+= delayTime;
    
}
  
}

  
public void doExit()
  
{
    robot.
disconnect();
    isRunning 
= false;
    
notifyDestroyed();
  
}
}

   

Download JAR/JAD files for installation on mobile phone


Discussion: The class library NxtJLibMP for J2ME is based on the Gidlet framework, but for the user it looks exactly the same as NxtJLib for the J2SE (same JavaDoc). Within the Gidlet framework an application class for J2ME extends Gidlet and overrides main() that is called in a separate thread when the MIDlet starts and replaces the usual application main(String[] args) method. Keep in mind that the environment is not fully initialized until main() runs, so do not create an NxtRobot instance as instance variable or in a static initializer block.

There are several forms and displays available, in particular a very handy MConsole that looks like System.out with overloaded print/println() methods for all primitive data types and a horizontal and vertical scroll ability using the mobile phone's cursor keys. See the JavaDoc of the Gidlet framework for more information.

As usual for MIDlets, every Gidlet display has an exit soft button that is automatically showed. When pressed, the MIDlet is shut down by calling notifyDestroyed(), but the user is responsible that the main() method terminates. If the MIDlet connects to an NXT brick, disconnect() should be called before closing down the MIDlet. Otherwise the NXT brick must be turned off and on before the next connection is possible. As seen in this example you can override doExit() to handle the shutdown code yourself.

Many internal options of NxtJLibMP are user-definable. They are read at runtime from a property file nxtjlib.properties. This file must reside in the subdirectory res of the jar-archive. You find it when you unpack the downloaded jar file.

(Because of the Bluetooth link, MIDlets using NxtJLibMP do not run on the WTK emulator. This makes the development more subtle than usual. Due to the similarity of NxtJLib for J2ME and J2SE we recommend to emulate the host-to-brick communication section of your J2ME program with a J2SE test program.)

 

Forth example: Remote Control (NxtJLibMP for Mobile Phones)

Purpose: In practice many robots are controlled by a remote control device, operated by a robot pilot. For an NXT rover, it is very elegant to use the wireless Bluetooth communication link of a mobile phone to steer the robot. In our example the rover can be moved using the cursor keys with the following assignments:

  • up: Increase speed (like pressing the gas pedal)
  • down: Decrease speed (like releasing the gas pedal)
  • left: Increase left gyration (like turning the steering wheel to the left)
  • right: Increase right gyration (like turning the steering wheel to the right)
  • fire button: Move straight on (no gyration)
  • OK soft button: Reverse direction

The class Gear is very useful to perform these actions, in particular because leftArc() and rightArc() perform a gyration with a radius that is independent of the speed.

// RemoteControl.java

import javax.microedition.lcdui.*;
import ch.aplu.gidlet.*;
import ch.aplu.nxt.*;

public class RemoteControl extends Gidlet
{
  private NxtRobot robot;
  private MPanel p;
  private Gear gear = new Gear();
  private int speed = 0// -100..100
  private int steeringAngle = 0// -10..10

  public void main()
  {
    robot = new NxtRobot(nullfalse);
    if (!robot.connect())
      return;
    robot.addPart(gear);

    p = new MPanel("NXT Control"01000100);
    p.show();
    p.bgColor(BLUE);
    p.color(WHITE);
    p.addKeyListener(this)// Activate cursor keys
    p._label(" Cursor keys to navigate"70);
    p._label(" Fire to go ahead"90);
    p._label(" Rev to reverse"110);
    p.addOkButton("Rev");
  }

  public void keyPressed(int keyCode)
  {
    keyRepeated(keyCode);
  }

  public void keyRepeated(int keyCode)
  {
    int sStep = 4;
    int aStep = 1;
    boolean ok = false;
    switch (p.getGameAction(keyCode))
    {
      case Canvas.FIRE:
        steeringAngle = 0;
        ok = true;
        break;

      case Canvas.UP:
        speed += sStep;
        if (speed > 100)
          speed = 100; // Limit
        ok = true;
        break;

      case Canvas.DOWN:
        speed -= sStep;
        if (speed < -100)
          speed = -100; // Limit
        ok = true;
        break;

      case Canvas.LEFT:
        steeringAngle += aStep;
        if (steeringAngle > 10)
          steeringAngle = 10; // Limit
        ok = true;
        break;

      case Canvas.RIGHT:
        steeringAngle -= aStep;
        if (steeringAngle < -10)
          steeringAngle = -10; // Limit
        ok = true;
        break;
    }
    if (ok)
      setMotion();
  }

  private void setMotion()
  {
    gear.setSpeed(Math.abs(speed));
    if (speed >= 0)  // forward
    {
      if (steeringAngle > 1)
        gear.leftArc(1.0 / Math.abs(steeringAngle));
      else
      {
        if (steeringAngle < -1)
          gear.rightArc(1.0 / Math.abs(steeringAngle));
        else
          gear.forward();
      }
    }
    else  // backward
    {
      if (steeringAngle > 1)
        gear.leftArc(-1.0 / Math.abs(steeringAngle));
      else
      {
        if (steeringAngle < -1)
          gear.rightArc(-1.0 / Math.abs(steeringAngle));
        else
          gear.backward();
      }
    }
  }

  public void doOk()
  {
    speed = -speed;
    setMotion();
  }

  public void doExit()
  {
    robot.disconnect();
    notifyDestroyed();
  }
}


   

Download JAR/JAD files for installation on mobile phone


Discussion: To simplify the code we use the Gidlet framework extensively, especially the class MPanel that provides a graphics display with a KeyListener (consult JavaDoc for more information). The callback method keyPressed() is invoked each time a key is actuated. The method keyRepeated()is invoked at regular intervals when the key is held down. We use an instance variable speed that represents the velocity of the rover (positive: forward motion, negative: backward motion) and steeringAngle that represents the gyration angle of the steering wheel (positive: left gyration, negative: right gyration). It is good to know, that leftArc() and rightArc() with a negative radius gives a backward motion.

Since the Gidlet's doOk() is called when the OK soft button is hit, we override it to reverse the sign of the speed.

You need some practice to become a good robot pilot. Enjoy.