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 better -- certainly more robust -- language than C/C++ and more standard than C#. The computer we chose, LattePanda (LP) has an on-board Arduino co-processor for driving servos and other timing-critical hardware control. It also runs Windows/10, and Win10 runs Java, but the LP interface to the Arduino is specified and written in C#. The Arduino itself came pre-programmed with (open-source) Firmata, and my original purpose was to avoid re-inventing that wheel, but rather only to replace the supplied LP driver with a Java equivalent I called "FakeFirmata" (FF).
As described elsewhere, we outfitted the speed control of the car with a DeadMan switch so when the code got lost, the person holding the original remote transmitter could stop the car by releasing the trigger. The program director didn't like the clumsy wiring harness required to make it work and suggested altering the Arduino code to do the same thing. It turned out to be harder than he supposed, but here is my take on it. Supposing that the name "Firmata" might be Italian for "firmware" I chose to name my rewrite "HardAta" to reflect the fact that the pin numbers in this build are "hard-coded" constants. This is because Arduino is "open source" and therefore necessarily quite opaque, so I was unable to find sufficient documentation on The Way Things Work inside it to be able to support arbitrary pin numbers for the inputs and outputs as does Firmata. You can still re-assign them, but you must rebuild and re-install the modified code using the Arduino programming tool that came with the LP (or whatever you would be using to program your Arduino otherwise).
We originally were using the Arduino only to drive two servos, so most of the Firmata interface is not implemented, but (originally) only the two APIs to blink the on-board LED and to drive servos. The DeadMan switch is hard-coded to a particular input. We also needed a pulse counter to be used to count the driveshaft turns (as an odometer and speedometer). These I added to HardAta as additional pin modes. I have no idea what the original Firmata will do to you if you sent it these new pin modes or the extra debugging commands I added; my code (which does not implement the analog inputs and outputs) simply ignores modes it does not recognize, but unixy programmers tend to be much less forgiving.
The LP C# code is well-commented, and with passing reference to the Firmata C++ source code ("open source" code is intentionally very opaque and unreadable, so this is no easy task), and mostly the Firmata documentation on GitHub, adding the additional APIs should be straight-forward knowing only Java and neither C nor C#. I originally tried to preserve the LP text as much as possible, changing only the spelling as appropriate to Java syntax. Some of that went away as I abandonned their design.
My initial efforts at making input work failed,
and analog inputs and outputs are still unsupported in this release, but
I did get it to count pulses as a digital input, and the DeadMan switch
seems to work, 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. The Java-side
driver still has the code hook (SimHookBase) to give the TrakSim
simulator look-only access to the signals being sent to the servo controls.
Download FakeFirmata (includes HardAta.ino)
FakeFirmata web pages
APW2 Technical Topics
TrakSim Web Pages
To install HardAta, just open the "HardAta.ino" text file in the Arduino.cc development program (which is already pre-installed in LattePanda, so double-click does the right thing) then click on the right-arrow "Upload" button and wait for the Arduino blue LED to throb its power-on greeting and then blink the HardAta hello sequence (Morse code "HA"), then run your program. The "Firm" part of the Firmata name is probably in reference to "firmware" -- which is sort of halfway between software and hardware, that is, it is programmed and compiled as code, but does not evaporate when you turn the power off. Every time you power up the LattePanda, HardAta will start up and be ready to use, until you replace it with something else.
Any time you download a new Arduino install, it automatically restarts
the Arduino in the new download. You should be aware, however, if your
LattePanda Java program is running and using FF to transfer data to and
from the Arduino, it uses the same serial port that the Arduino.cc program
needs for downloading; one of them will fail if you try to run them both
at the same time, that is, if you don't quit one to run the other.
I think of the first byte as a command from the host computer to the Arduino, telling it to do something. The remaining two bytes in the packet are parameters to that command, or else unused fillers. Some of the commands apply to a particular input or output pin on the Arduino, and that pin number is encoded in the low four bits of the command byte, leaving the whole remaining two bytes for 14-bit data (two 7-bit bytes).
These are the commands defined in HardAta, shown in hexadecimal. When the second digit is zero, that is where the pin number "p" goes. The two parameter bytes are usually shown here as a single "value" encoded Little-Endian (the least significant 7 bits in the first byte and the most significant additional bits (sometimes only one bit in the case of 8-bit data) in the second byte. Exceptions are noted.
90 -- DIGITAL_MESSAGE p,value
The four-bit "p" is a port number encoded in the command byte -- the Arduino so far has at most three or four 8-bit ports apparently numbered 0-3 for A-D, but the documentation I found on the Arduino.cc website does not match the Leonardo chip that came with this LattePanda, so I really don't know -- and the value is an 8-bit value (split as described above, with the low seven bits first, the the additional bit in the second byte) to be delivered to the whole port. The caller is responsible for aggregating individual bits of output into a complete port data byte. Ones in the byte drive the output pins to the high voltage (approximately 5 volts) and zeros drive it low (0 volts). Most of the individual digital pins I could not find on any documented port name and the others were not mapped in any logical way, so I gave up and support only D13 (the blue LED) as an output.A0 -- REPORT_PULSECOUNT p,0,0You must previously designate a pin as OUTPUT using SET_PIN_MODE for DIGITAL_MESSAGE to control the voltage coming out on the connector. Servo pins are programmed with a different value to SET_PIN_MODE, and HardAta will override whatever outputs you designate for pins programmed as servo outputs.
(New in HardAta) The four-bit "p" was intended to be a pin number, but I cannot find a credible pin-to-port mapping, so the pin number is ignored and hard-coded to a single digital pin, which seems to work as documented. This command returns a three-byte packet consisting of the same command byte followed by the current pulse count encoded with the low 7 bits first, and any remaining bits in the second byte, and then resets the count to zero. The pin must have been programmed in SET_PIN_MODE as PULSECOUNT.D0 -- REPORT_DIGITAL p,0,0
The four-bit "p" is a port number encoded in the command byte, and this command returns a three-byte packet consisting of the same command byte followed by the 6-bit input value from Port B (which is defined only for pins 8-13) and a filler byte. Only those bits not designated as outputs are valid in the response. The two data bytes following the command (shown above as "0,0") are required by HardAta but ignored. FakeFirmata does not support this command.E0 -- ANALOG_MESSAGE p,value
The four-bit "p" is supposed to be a pin number, but apparently the Servo library in the Arduino stuff is only guaranteed to work with D9 and D10, so I only support those two pins. This command sets the servo PWM for the specified pin to the width corresponding to the 8-bit parameter value (between 0 and 180 degrees) packed into the two data bytes. The pin must have been programmed in SET_PIN_MODE as SERVO or DM_SERVO.F4 -- SET_PIN_MODE pp,mode
The first parameter byte "pp" is a pin number (0-15, but HardAta supports only Port B, pin numbers 8-13) and this command designates that pin as either input or output according to the mode set from one of the following options. Once a pin has been programmed, HardAta cannot change it until the Arduino is reset (or reloaded). Additional SET_PIN_MODE commands for the same pin will be ignored.F9 -- REPORT_VERSION 0,0
0 -- (Digital) INPUT -- FakeFirmata does not support this pin mode.1 -- (Digital) OUTPUT -- HardAta only supports this pin mode for pin D13.
4 -- SERVO -- HardAta generates a servo PWM signal using the Arduino Servo.h library (which generates a pulse train with a 25ms period), where the pulse width varies from 1ms to 2ms dynamically according to the value given in ANALOG_MESSAGE from time to time. You may designate one or two pins as servos.
5 -- DM_SERVO -- (New in HardAta) This is the same as SERVO, but if you also designate another pin as DEADMAN, this servo will be clamped to the midpoint 1.5ms (steering straight ahead or motor stopped) when the DeadMan input has no signal, or if its pulse width is less than 1.7ms (the trigger on the Traxxas remote pulled about halfway or less). Either or both servo pins may be subject to the DeadMan control, depending on whether you use SERVO or DM_SERVO .
6 -- DEADMAN -- (New in HardAta) This pin is designated as a servo-style PWM input, which must be present and active (greater than 1.7ms) for any DM_SERVO outputs to be driven by your software other than the center 1.5ms position.
7 -- PULSECOUNT -- (New in HardAta) This pin is designated as an input pulse train, which HardAta counts the pulses as they come in, and your control software can read (and reset) this count from time to time using the REPORT_PULSECOUNT command. Only one pulse train can be counted.
This command returns a three-byte packet consisting of the same command byte followed by the major version number in the first data byte and the minor version number in the second data byte. HardAta reports these as year and month (18,11 means 2018 November) to prevent confusion with the real Firmata numbering system. The data bytes following the command are required by HardAta but ignored. If the version number returned is 18 or greater, then you can safely use the new HardAta commands. You can use FakeFirmata access method GetFirmwareRev() to send this command.FA -- REPORT_INFO 0,0
This command turns on the verbose logging mode in HardAta, so that accepted commands are acknowledged (echoed), and additional informational packets may be generated from time to time. You can use FakeFirmata access method LogHardAta() to send this command.FD -- REPORT_MISCELLANY opt,data
This command selects one of the extras options designated by opt, and returns one or more packets of result data as defined by that option. The following option is defined in this release, but you could add more.126 -- HardAta originally used a fast algorithm for calculating the 1000-2000 pulse width from the 0-180 parameter, and I was interested in how much faster it is than the built-in Arduino library call. This option runs the calculation both ways for the (0-127) value in the second data byte, then reports both times.
Unfortunately, the data link between the Arduino and the LattePanda is not exposed (and may not even be intelligible if it were), so I had to invent my own data stream for viewing in an environment where I was not even confident in my Java code on the Windows side. So in this implementation is a "bit-banger" asynchronous transmitter that drives the blue LED pin with whatever data I want to view on an oscilloscope. It is currently optioned out.
The PWM specification designed for Firmata gives about a 5 microsecond resolution on the pulse width of the output servo PWM train. What I found on the internet seemed to say that various models of Arduino run at a clock speed resulting in two or four microseconds per instruction. By way of comparison, a typical laptop does instructions at the rate of a half nanosecond each (8000 times faster), whereas the first mainframe I programmed, the IBM 704, had an instruction time of 24 microseconds, and the Intel 4004 (the first microprocessor, which I also did a lot of work on) had (I think it was) an 11-microsecond instruction time. You can attach an oscilloscope probe to the bus on these babies and watch the data go by. I did a lot of that. Arduino does not bring its bus off-chip, so there's nothing to attach to. But it's not hard to blink the blue LED and attach an oscilloscope probe to it (in the connector). I did that.
One of the reasons we have an Arduino on the LP system is that bloated operating systems like Windoze (or the even slower unix variants like Linux) cannot control signals to the precision required for hardware such as servos. You get that precise timing on machines running much closer to the resolution the hardware needs by carefully watching every cycle. It's harder when the only tool you have is a C compiler with no disassembler, so you don't even know what the compiler is doing. Some of the remarks I found on the internet suggested that it's available, but didn't say how or where. But I know what compilers do, and I can make inferences.
The most critical timing is the servo PWM, which resolves to about 5 microseconds. The first time I looked at the output from my code, my basic loop was running in 25 microseconds. Obviously the instruction time is closer to one microsecond than two or four, which is good, but my pulse width resolution is still five times worse than the theoretical best.
Then I discovered a very opaque "Servo.h" library of servo
driver code designed to use the (poorly documented) Arduino interrupt hardware.
So that's what this implementation uses. The Firmata code that came with
the LP knows more than they are telling (and more than I want to take the
time to reverse-engineer) about how the "Servo.h" library can
be used for arbitrary pin numbers, so I cannot do a generalized version
that works the same but only adds our DeadMan switch and pulse counter.
Thus the "hard" (as in "hard-coded") in the name "HardAta". I leave repairing
it as the proverbial "exercise for the student."
DeadThresh = 1700
The DeanMan switch is assumed to be the throttle trigger on the R/C
remote, if it's not pulled, the car is dead. The neutral position on a
normal transmitter PWM is 1.5ms and fully pulled is
closer to 1ms, so the halfway point is about 1.7ms (1700 microseconds).
Your mileage may vary, and you can tweak this number appropriately.
NoSigTimeOut = 1000/30
If the transmitter is out of range, or the battery died, HardAta will
keep looking for about one second (1000ms) before declaring it dead, but
you can adjust this constant up or down to your liking. Numbers are only
16 bits, so you have an upper limit of about 16 seconds (16383/30) to keep
in mind, but that is far too long to be useful as a DeadMan switch.
PulseCntPin = 8, Servo9pin = 9, ServoApin = 10, DeadManPin =
11
The four I/O pins used by HardAta are hardwired in these four constants.
If you are not using one of these features, you can recompile it with that
pin set =0. Or you can choose another pin number, but I only tested these
four values.
I also wrote a bunch of support subroutines (think: "methods") to do
things I needed more than once, or in a few cases, to support some complex
activity that I could pull out and define separately. Subroutine calls
take extra time, so you want to use them sparingly if you care about the
time through your code.
void BlueUART(int whom)
In the Goode Olde Dayes, when we wrote our own I/O code, the de-facto
standard user interface device was a Teletype ASR-33.
Popular
Electronics (the magazine all the hobbyists read back then for hardware
ideas) ran an occasional article on how to build your own "TV Typewriter"
which amounted to a keyboard and video interface to the antenna input on
a TV set (back when TVs were analog and understandable) and a serial interface
that pretended to be a Teletype, only faster. The more expensive computers
had a hardware "UART" (Universal Asynchronous Receiver-Transmitter)
chip that ran the same protocol as the Teletype and TV Typewriter, and
the rest of us did it in software. It wasn't hard, you just had to be careful
with the timing. Just like Arduino. Anyway, BlueUART is
basically the same thing, except output only. You give it an 8-bit data
byte, and it bangs the blue LED on and off with 100us
bits, and if you have an oscilloscope attached to D13 (top-middle hole
in the 24-pin connector on the front of the LP board) and ground, it will
trigger a sweep each time your program sends an output, and you can read
the bits off across the scope screen. After I got my code working, I optioned
it off, but left the code in.
If you set global constant BlueOptn negative, then all the test code I put in to call the BlueUART method will activate. There are assorted positive constants you can set BlueOptn to, and each one gives a different significance to the blue LED, as noted in the comments. In the first release the blue LED tracks the DeadMan switch (on when dead). Set BlueOptn to zero to completely disable it.
void Q3by2LP(int whom, int fust, int lust)
My limited version of the Firmata protocol is exactly three bytes for
each message, each way. On the LP, the Arduino seems to be connected to
the host Win10 computer by some kind of opaque fake UART
(rumor has it that it's a USB implementation), with
a library "Serial" object that has write()
and read() methods that are pretty fast. Before I knew
all that, I set up a buffer to queue my output, then send it when the hardware
was ready. Q3by2LP takes a standard 3-byte message and
queues it in the buffer, then returns immediately.
void UpServo(int pin, int info)
Driving the servo library isn't particularly hard, but when you factor
in the software DeadMan switch, I decided it would be simpler to pull the
decision out into a single place.
void UpDeadMan(boolean yup, int when)
Each time we notice a falling transition on the DeadMan input pin we
measure the pulse width to decide if it's dead or not; if either the rising
or falling edge takes too long coming, we assume it's dead. UpDeadMan
captures that information and sends a message to the host Java program
(if requested) and tracks the state on the blue LED
(likewise).
int PulsePins(int whom)
void ReadPins()
Back when I was trying to figure out the pinouts on the Arduino, I
wrote these two subroutines to help me figure out where the data is. PulsePins
outputs on each output pin a sequence of pulses representing the pin number
(eight pulses are output as one longer pulse). That way I couldwalk down
the connector with the scope probe and count the pulses I saw, and know
exactly which logical pin number I was looking at. ReadPins
used BlueUART to display all the
digital inputs in sequence, so I could take a low or high voltage, and
poke it each alleged input and see which bit followed it up or down on
the scope. I suspect the timing is erratic, because some of the holes I
poked a wire into, several bits went up or down at the same time. Like
I said, I gave up on it, but I left the code in so I could change my mind
on how much effort this might be worth.
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.
Rev. 2019 May 18