FakeFirmata:

Driving Arduino from LattePanda in Java

Purpose & History

We are doing this summer program for high school students, in which they get to drive a radio-controlled car modified to be driven autonomously from an on-board computer with attached camera. The first year they learned how to extract objects ("pedestrians") from the video feed, then they controlled the steering and speed by means of servos driven from an Arduino connected to the main computer. This year it gets more complicated.

Most of the students have learned Java in school, and (except for a few application areas that the Java designers didn't want to touch) it's a more robust language than C/C++ and more standard than C#. Furthermore (if you are willing to take the reasonable and modest steps to avoid triggering a garbage collection time-out) the Java JIT compilers in most implementations produce code that runs essentially at machine speed, about as fast as C/C++.

The computer we chose, LattePanda (LP) runs Windows/10, and Win10 runs Java, but the LP interface to the Arduino is specified and written in C#. The Arduino itself is programmed with (open-source) Firmata, and the purpose of this package is to avoid re-inventing that wheel, but rather to replace the supplied LP driver with a Java equivalent. I call it "FakeFirmata" (FF).

We originally were only using the Arduino to drive two servos, so most of the Firmata interface was not implemented, but only the two APIs to blink the on-board LED and to drive servos. The LP code is well-commented, and with reference to the Firmata C++ source code, the Firmata documentation on GitHub, adding the additional APIs should be straight-forward knowing only Java and neither C nor C#. I tried to preserve the LP text as much as possible, changing only the spelling as appropriate to Java syntax.

Unfortunately, there is no standard way in Java to access the serial port that Arduino uses for communication to the host computer. There are several serial port implementations, and every one I looked at is huge and hard to understand and overkill for this application, but I did not want to re-invent that wheel either, so we chose the Java-Simple-Serial-Connector (JSSC, also available on GitHub) implementation as being slightly more transparent. After a hiccup or two (readily identified in the Java debugger and fixed by making sure their DLL is in the correct folder), I was able to make my test program blink the blue Arduino LED and thereafter to drive servos. I uploaded my original working code to the LP User Forum.

Everything tends to grow by "feature creep," and our application is no exception. So when I tried to allow for digital and analog input, I had to make some substantial revisions. My efforts at making input work failed, so what you see in this release are the original two output modes only, not much different from the initial version uploaded to the LP User Forum, except I added the DeadMan input processing and a pulse counter, plus a sandbox area for learning about the Arduino. It still has the code hook (SimHookBase) to give the TrakSim simulator look-only access to the signals being sent to the servo controls.
 

Related Links

Download FakeFirmata (includes JavaFlyCam and HardAta)
HardAta: Alternate Arduino Clone for LattePanda
JSSC on GitHub
Starting Up LattePanda
Fly2cam Notes
APW2 Technical Topics
TrakSim Web Pages

Important Notice

The JSSC (Java Simple Serial Connector) driver we used for this implementation has been known to not properly close out its serial port unless you call its closePort() method when you are done. Failing to do this (like if you terminate your program in the debugger) can leave the port inaccessible, and Win10 could allocate a different "COM" identifier when you try again. Because FakeFirmata uses a constant "COM3" port identifier, it may fail unless you reboot the computer after every abnormal termination. More recent change logs on their website report fixing something like that, so it may not still be a problem.
 

Using FakeFirmata

The API for FakeFirmata is intentionally the same as the LattePanda implementation, so mostly you just follow their documentation, the relevant parts of which I reproduced here as modified for Java and FakeFirmata. This is necessary because they seem to have "improved" their website so that it is no longer useful for understanding basic LP input and output. Fortunately I kept a copy of their text (but not the pictures).
 

FakeFirmata

FakeFirmata is an open-source Firmata library to replace the one that is provided by LattePanda; it is suitable for Java apps developed in Windows. This class allows you to control Arduino GPIO from Java apps, with features including:
* Writing to digital pins
* Controlling servo motors


3 Steps to Your Remote Arduino Project

1. Set up your PC
2. Set up the Arduino (It is pre-installed, unless you changed the Arduino program)
3. Create a project or use the sample project

Functionality

Constructor

* public Arduino();

There is only one constructor, and it sets the parameters as defined by LP to their default values, which is the only way it will run anyway.
 

Configuration

* public void pinMode (int pin, byte mode);

Sets the mode of the specified pin as you wish

Parameters

pin: the number of the pin whose mode you wish to set
mode: Arduino.OUTPUT, [Arduino.INPUT, Arduino.PWM,] Arduino.SERVO
New this release: Arduino.DM_SERVO, Arduino.DEADMAN, Arduino.PULSECOUNT

Digital

* public void digitalWrite (int pin, byte value);

Write to a digital pin that has been toggled to output mode with the pinMode() method

Parameters

pin: The digital pin to write to
value: Arduino.HIGH, Arduino.LOW

Servo

* public void servoWrite(int pin, int angle);

Write the angle to specified pin which has been set to Servo (or DM_SERVO) mode

Parameters

pin: Servo output pin.
value: Angle from 0 to 180.

Examples

digitalWrite


In this example, we will blink the LED which is connected with digital pin (D13)

API Required :

1. public Arduino();
2. public void pinMode(int pin, byte mode);
3. public void digitalWrite(int pin, byte value);


Hardware Required:

1. LattePanda x 1
2. led x 1 (you can use the LED attached to pin 13 on the Arduino board itself)


Circuit:

1. LED inserted directly into pin 13


Code:

1. Create a new project in [Eclipse], see New Java Project (below)
2. Main function code:
package blinkYourBoard; // project name

import java.lang.Thread;
import jssc.SerialPort;
import fakefirm.Arduino;

class Program {
  static Arduino arduino = new Arduino(); // create an instance
    // and initialize with the default parameters

  static void main() {
    arduino.pinMode(13, Arduino.OUTPUT);
      // Set the digital pin 13 as output
    while (true) { // ==== set the led on or off
      arduino.digitalWrite(13, Arduino.HIGH); // set the LED=on
      Thread.sleep(1000); // delay one second
      arduino.digitalWrite(13, Arduino.LOW); // set the LED=off
      Thread.sleep(1000); // delay one seconds
    } // end while
  } // end main
} // end class Program


Test:

1. Click [Debug or] Run button to execute, the LED will start blinking.

Servo


In this example, we will sweep the servo motor back and forth across 180 degrees.
Note: only digital pins 9 and 10 work as servo outputs in HardAta.

API Required:

1. public Arduino();
2. public void pinMode(int pin, byte mode);
3. public void servoWrite(int pin, int angle);


Hardware Required:

1. LattePanda x 1
2. Servo Motor x 1


Circuit:

1. Servo inserted directly into pin D9:

[This is my drawing, because I did not save a copy of their drawing, and it's all gone now. Anyway, I think their drawing suggested you plug it into the black header using jumper wires, whereas the servo cable connector is an exact fit to the white pins on the other side of the LP board.]

[The LattePanda website didn't say so, but the current draw of an active servo can spike the voltage level on the LP when minimally powered (like using the power brick they provided) so that other circuits (not necessarily the LP itself) became marginal. We found it useful to cut the (red) power wire of the servo cable and provide power to the servo directly from a separate power source with better capacitance (like a 5v USB phone-charger battery).]

Code :
1. Create a new project in [Eclipse], see New Java Project (below)
2. Main function code:
package servoExample

import java.lang.Thread;
import jssc.SerialPort;
import fakefirm.Arduino;

class Program {
  static Arduino arduino = new Arduino(); // create an instance
    // and initialize with the default parameters

  static void main() {
    arduino.pinMode(9, Arduino.SERVO);
    while (true) {
      arduino.servoWrite(9, 180);
        // tell the servo motor go to position 180 degrees
      Thread.sleep(1000); // delay one second
      arduino.servoWrite(9, 0);
        // tell the servo motor go to position 0 degrees
      Thread.sleep(1000); // delay one second
    } // end while
  } // end main
} // end class Program


Test:

1. Click debug to execute, you will find the motor sweeping forth and back continuously.

Using the New Features

New APIs

int GetFirmwareRev()
Returns two 16-bit numbers packed into a single int, the major revision in the high half, and the minor revision in the low half. This works with the standard Firmata also, but with HardAta, the major revision is the 2-digit year (18 for 2018) and the minor revision is the month (12 for December) so the first release is 0x12000C (2018 December), and subsequent releases will always be higher. Last I looked, Firmata major revision was less than 4 (0x40000).

void LogHardAta()
Turns on a verbose mode in HardAta, which makes it easier to see what the Arduino is doing. You may need to designate a listener if you want your Java code to see these, or you can turn on the logging switch SpeakEasy=true to see them on the Java runtime console.

interface UpdateListener
void addInputListener(int selector, UpdateListener whom)
Implement this interface in a listener class, and tell AIL about it to catch any of the following types of selector events:

Arduino.REPORT_VERSION -- to get the current version = 0xF9 (249)
Arduino.DEADMAN_MESSAGE -- to see DeadMan transitions = 0xFB (251)
Arduino.REPORT_MISCELLANY -- to get other HardAta output = 0xFD (253)
Arduino.REPORT_PULSECOUNT -- to get current pulse count
Arduino.REPORT_DIGITAL -- (unsupported) digital input
Arduino.REPORT_ANALOG -- (unsupported) analog input
You can only designate one listener for each selector, but you can use the same listener for more than one selector. There is an example listener in each of the two examples (Counting Pulses and Using the Remote As DeadMan) below, and a more comprehensive example in the TrakSim (DrDemo) example code.

The UpdateListener interface requires you to implement this method which is called for every event of the selector type:

void pinUpdated(int pin, int value)
The VERSION, DEADMAN, and MISCELLANY selector types report that reference number as the "pin" parameter; otherwise the designated input pin number is passed in this parameter. The value parameter is generally a 14-bit value specific to the event.


void pinMode(int pin, byte mode)
This is essentially the same as the original Firmata method, but extended to accept three new pin modes:

Arduino.OUTPUT     -- pin=13
Arduino.SERVO      -- pin=9 or pin=10
Arduino.DM_SERVO   -- (new) pin=9 or pin=10
Arduino.DEADMAN    -- (new) pin=11
Arduino.PULSECOUNT -- (new) pin=8


The original Arduino.INPUT, Arduino.ANALOG and Arduino.PWM pin modes are supported only in the original Firmata Arduino code, and not in HardAta. HardAta only supports these five pins in these modes.

void digitalWrite(int pin, byte value)
This is essentially the same as the original Firmata method, but implemented in HardAta only for the blue LED D13 (pin=13). The value should be either Arduino.LOW (off=0) or Arduino.HIGH (on=1).

void servoWrite(int pin, int angle)
This is essentially the same as the original Firmata method, but in HardAta only works with the designated servo pins D9 and D10. The servo angle should be a number between 0 (full left or full reverse) and 180 (full right or full forward). If the pinMode of this servo is set using Arduino.DM_SERVO, then when the DeadMan trigger is not activated, HardAta will substitute an angle=90 (stopped or straight ahead), and restore the given angle when DeadMan is activated again.

void DoPulseCnt(int pin, int ms)
This turns pulse counting on and specifies an approximate period (in milliseconds, 1000 is one second) to automatically generate reports, which you should catch using addInputListener. HardAta currently only works with pin=8. The actual period seems to vary by 100ms or more, depending on other events in the Java eco-system, which may impact your speed calculation; for better speed analysis you probably should use Gaussian smoothing across multiple periods.

void Open()
void Close()
These are essentially the same as the original Firmata methods, which opens the serial port connection and generally starts things up, and then closes things down when you finish. We had some trouble with the Win10 handling of the serial port through JSSC if Close is not called when the program quits, but that may be fixed (in JSSC) now. It's still a good policy to close things down properly when done.

void Send3bytes(int comm, int data, int more)
This is a convenient way to send a 3-byte command packet to HardAta. Normally you don't need this for ordinary usage.

static int GetMills()
static String FormatMillis(String prefix, int now)
Sometimes I found it useful to time-stamp my diagnostic log with an easily read number of milliseconds from program start. GetMills returns that value as an integer for relative time calculations, and FormatMillis can format it as part of a System.print line.
 

Examples

Counting Pulses

Enable pulse counting using a pinmode call. You probably need to set up a listener object to catch the (asynchronous) pulse counts from the Arduino. Then periodically, you send a request for the current count and catch the result.

We found that the driveshaft turns sensor on the Traxxas car has a limited supply voltage operating range, and it completely shut down on the +5v provided by the LP I/O connector, but it operated fine on the +3.3v provided by the radio receiver when it was powered by the ESC. However if the receiver was powered by the LP +5v, it only gave +3v to the driveshaft sensor, and the resulting pulse train was uncomfortably close to the specified +3v threshold for the LP/Arduino digital input. See the circuit diagram for the DeadMan test (below)

For this test I left the receiver connected to the Electronic Speed Control (ESC) to drive the car, and tapped off only the driveshaft sensor signal ("RPM" and ground) to connect to the LP/Arduino digital input D8 while leaving it still connected to the receiver as shipped from Traxxas, as shown here:

The relevant code is much more complicated than driving a servo, but this should give you the basic idea. It captures the turns count every second and displays it on the console:
 

package testPC; // project name

import java.lang.Thread;
import fakefirm.Arduino;
import fakefirm.UpdateListener;

class Program {
  static Arduino ardueeno = new Arduino(); // create an instance
      // and initialize with the default parameters

  private static class testListener implements UpdateListener {
    int prior; // previous reading

    public void pinUpdated(int pin, int value) { // called when it arrives
      boolean doit;
      if (pin==8) {
        if (value+prior>0) SystemDebugLog("Pulse count = " # value);
        prior = value;}~if
      } //~pinUpdated

    public testListener() { // constructor, init..
      prior = 0;
      SystemDebugLog("new testListen");}} //~testListener

  private static testListener ArduPinUpd = new testListener();

  static void main() {
    int whom = Arduino.REPORT_PULSECOUNT;
    if (ardueeno.GetFirmwareRev()<0x120000) return; // not HardAta
    ardueeno.addInputListener(whom,ArduPinUpd); // set listener
    ardueeno.pinMode(8,Arduino.PULSECOUNT);
      // Set the digital input pin 8 as pulse count
    ardueeno.DoPulseCnt(8,1000); // get results each 1000ms
    try {while (true) Thread.sleep(500); // delay half second
      } catch (Exception ex) {System.out.println(ex);}} // end main
} // end class Program

Using the Remote As DeadMan

Enable the DeadMan feature using a pinmode call, then enable each servo under its control using DM_SERVO instead of SERVO. See the diagram for connecting up the receiver and servo(s) You may wish to set up a listener object to catch when the DeadMan switch is active or released.

The circuit shown here also includes the pulse-count tap for the full computer/car connection, except the steering is not shown because this circuit was tested on the bench without actually controlling the steering.

Note that "D8" pin goes into the black header on the LP board on the opposite edge from the white servo headers, second hole in from the inside corner nearest to the LP (white) D9 servo header (see the diagram above) and the "GND" can plug into outside (ground) pin of any vacant (white) servo header -- or if you still have the pin on that wire from the "Counting Pulses" experiment (above), it plugs into the inside row of the black connector at the far end from the D8 hole, as shown in the diagram above. I had a link to the LP website with a diagram where they labelled all the pins, but they changed everything and I can no longer find that diagram on their website, but you might try the "First Edition Hardware Introduction" page.

The code here turns on the throttle at a moderate speed, and leaves it on while the operator pulls and releases the speed trigger on the transmitter, to enable the car to go or stop. The program monitors the HardAta output and prints out the status on the console:
 

package testDM; // project name

import java.lang.Thread;
import fakefirm.Arduino;
import fakefirm.UpdateListener;

class Program {
  static Arduino ardueeno = new Arduino(); // create an instance
      // and initialize with the default parameters

  private static class testListener implements UpdateListener {
    boolean died; // true if DeadMan activated

    public void pinUpdated(int pin, int value) { // called when it arrives
      boolean doit;
      if (pin==Arduino.DEADMAN_MESSAGE) { // =251
        doit = (value >= 0x2000);
        if (doit != died) SystemDebugLog("DeadMan = " # doit);
        died = doit;}~if
      } //~pinUpdated

    public testListener() { // constructor, init..
      died = false;
      SystemDebugLog("new testListen");}} //~testListener

  private static testListener ArduPinUpd = new testListener();

  static void main() {
    int whom = Arduino.DEADMAN_MESSAGE;
    if (ardueeno.GetFirmwareRev()<0x120000) return; // not HardAta
    ardueeno.addInputListener(whom,ArduPinUpd); // set listener
    ardueeno.pinMode(11,Arduino.DEADMAN);
      // Set the digital input pin 11 as (PWM) DeadMan switch from xmtr
    ardueeno.pinMode(10,Arduino.DM_SERVO);
      // Set the digital output pin 10 as ESC servo under DeadMan control
    ardueeno.servoWrite(10,105); // start servo +15 degrees
    try {while (true) Thread.sleep(500); // delay half second
      } catch (Exception ex) {System.out.println(ex);}} // end main
} // end class Program

Using Eclipse

If you already have a Java development environment installed on your computer, you probably don't need to read this section. If you are just getting started, you need to install some kind of Java development environment. There are several, but I see the most references to IBM's Eclipse. It's been around for a decade or more, but it was still pretty flakey when I first started this in 2016; it seems to have gotten better since then. I wrote a whole web page on Installing Eclipse on Windows in a different context, you can read it there. It also tells you how to create a new Java project, which I reproduce here:
 

New Java Project

After you have installed Eclipse (or restored it from a backup), you can start to work in Java. You need to close the welcome screen (if it is showing) to get on with life. First you need a project: from the File Menu -> New -> Java Project. It opens up a dialog to name your project, then Finish. Then you need a new package, which is from the same File -> New menu, or else click the little package icon on the toolbar. Java expects package names to begin lower case. Within that, you need a new class (same menu, or the "C" in a green circle). Now you are ready to start writing code.
 

License

FakeFirmata is copyright 2018 Itty Bitty Computers and released to the public as open source "as is" and (like every other software product, whether you get it free or pay for it) it has no warranties at all. Compile it with your self-driving code. If you have problems, ask me or fix it yourself. The distributed version ran at least once on the LP and where there is overlap, it seemed to match what the LP website seemed to say should happen, and otherwise it seemed to work in the car.

If you have questions, you can send me an email and I'll answer the best I can, but I may not have sufficient time or access to the necessary resources to test anything, so I cannot promise any particular improvements.

Tom Pittman

Rev. 2019 May 17