Monday, December 3, 2012
Google Web Toolkit - First Steps
Saturday, March 10, 2012
(Yet Another) Android NDK Blog Posting
This blog post provides an introduction to NDK along w/some anecdotes and a working Android application.
Note that I am writing this in March of 2012. The current SDK is version "r16" and the current ndk is version "r7b". My examples should be valid for the current versions. As always, these examples might not age well so YMMV.
Some of you might be surprised to discover that Java existed before Android (relax, it's a joke). Java has always provided a mechanism to integrate with C/C++ applications called JNI (Java Native Interface). Wikipedia provides a decent overview of JNI and Sun (now Oracle) provides a comprehensive introduction to JNI here. Serious developers will want to read the "JNI Specification" (also available of the Oracle web site.
At runtime, your C/C++ objects will be linked and executed using the JVM. There is a cost to transition the Java/C barrier, make sure you do enough work on the C side to make the transition worth while. Of course, if you include JNI then you have excluded portability (because the C/C++ files must be compiled for every target platform). Portability is not much of a concern for Android since most targets are ARM platforms.
In the early days of Java, JNI was a frequent requirement because many vendors had not yet created Java friendly libraries. The usual steps were:
- Create "native" Java methods to wrap around C functions. I usually collect these in a common class called "JniWrapper" (or "NdkWrapper" for Android).
- Use the "javah" utility to generate a C header file based upon the contents of "JniWrapper" (i.e. "javah -jni java.class")
- Write a C wrapper to act as a bridge between C and Java, the contents of the wrapper correspond to the generated header file (from step 2). Variable conversion, etc. between C/Java is typically performed here.
The above looks rather straight forward (and it is) but in practice some projects were quite difficult to complete and make stable (different threading models, etc). As Java came to dominate, there was better support for Java and less of a reason to employ JNI. Thanks to Android, this issue is receiving much more attention.
When I design a JNI application, I break the functionality down into three use cases:
- Java invokes C (most common example)
- C invokes Java (frequently asynchronous callbacks)
- Shared variables (visible to both C and Java)
I have created a sample Android NDK application to illustrate these use cases which can be found on GitHub (more about the example later).
Another design issue relates to using objects as arguments. In general, I try to avoid using complicated containers as arguments. Instead I favor primitive types such as int (not Integer). Strings are the exceptions and JNI provides convenience mechanisms for handling String.
Now create a Java class to act as the interface between Java and C. Your C methods will be invoked by Java methods marked as "native" (these look like "abstract" methods). This class should also contain a call to "System.loadLibrary()" which will cause your C/C++ objects to be loaded within the JVM.
The "javah" utility will generate a C header based upon "native" declarations within a Java class file. The resulting file looks something like this:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_digiburo_example_native_demo_NdkWrapper */
#ifndef _Included_com_digiburo_example_native_demo_NdkWrapper
#define _Included_com_digiburo_example_native_demo_NdkWrapper
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_digiburo_example_native_demo_NdkWrapper
* Method: nativeSetup
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_digiburo_example_native_1demo_NdkWrapper_nativeSetup(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Note these important features:
- The "jni.h" file which is distributed w/the JDK and provides Java data type definitions, etc.
- JNIEXPORT and JNICALL are macros defined within jni.h
- The C method name looks like a Java signature (i.e. package name, method name).
- The C function will always have at least two arguments: the "JNIEnv" pointer and the "jobject" reference. "JNIEnv" points to the virtual machine and "jobject" points to the Java class which invoked the native method (frequently described as similar to "this").
- Remember the JVM cares about method signatures. Item #2 and item #3 come directly from the "native" declarations in Java and must match exactly or your method will not be invoked.
While designing your NDK application note that only a small collection of libraries are distributed w/the NDK (library population varies w/NDK version). For example, I recently needed JPEG support but libjpeg.so is not part of the NDK. In this case you might have to build the libraries you need (be sure to build them using the appropriate cross compiler, etc). In my case I simply used the libjpeg headers and library from the Android platform sources. The point is: until you are a NDK master be sure to pad the schedule in case there is a surprise.
To integrate JNI w/your Android application is simple enough with recent versions of the NDK. Simply create an Android application in eclipse, then create a "jni" directory. The "jni" directory should be a peer to "src", "res", etc. and acts as the root directory for your C/C++ sources.
Eclipse also provides a C/C++ environment which will be handy for flipping between XML, Java and C/C++. My own preference is to do heavy lifting in emacs and my compiles on the command line.
To continue w/implementation, copy the "javah" generated header file to the "jni" directory and craft a source file to support it.
The Android NDK provides a build system which greatly simplifies integrating w/a Android application. Build directives are contained within the "Android.mk" and an optional "Application.mk" files.
#example Android.mk
LOCAL_PATH := $(call my-dir)
#
include $(CLEAR_VARS)
#
LOCAL_CFLAGS := -fexceptions
#
LOCAL_MODULE := digiburo-bridge
LOCAL_SRC_FILES := NdkWrapper.cpp
#
LOCAL_LDLIBS += -llog
#
include $(BUILD_SHARED_LIBRARY)
Note these import features:
- LOCAL_MODULE defines the name of your C/C++ library. This must match the name specified in System.loadLibrary().
- LOCAL_SRC_FILES define source files
- LOCAL_LDLIBS specifies link path (in this case to Android logging).
Compile within the "jni" directory by typing "ndk-build" which is a utility supplied w/the NDK.
Assuming a successful compile, you should see the results within your Android project in the "libs" and "obj" directories.
At this point you should be able to deploy and run your Android application using both Java and C.
I have created a sample Android NDK application called "NativeDemo" which is available from my GitHub repository. NativeDemo provides examples for:
- Java calls C (NdkWrapper.nativeSetup(), NdkWrapper.nativeString(), NdkWrapper.nativeAdder())
- C invokes Java (NdkWrapper.nativeVectorDemo() and NdkWrapper.callBack())
- Shared variables (NdkWrapper._nativeBuffer)
- Exception handling (NdkWrapper.exceptionDemo())
- Demonstration of JNI_OnLoad() and JNI_OnUnload()
- You need all the usual Android development tools such as eclipse, Android SDK, ADT plugin and of course the Android NDK.
- Import my git repository into your eclipse workspace.
- From the command line, enter the "jni" directory and type "ndk-build" assuming you have the NDK, etc this should compile NdkWrapper.cpp and place the results in the "libs" and "objs" directories.
- Now run the application from eclipse, it should deploy like any other application.
- To see the results, use "adb logcat" - note that log messages are generated both from Java and from NdkWrapper.cpp
Sunday, July 24, 2011
BeagleBoard for Android
There were several candidates but I quickly settled on the BeagleBoard-XM as sold by DigiKey for USD 149. (LiquidWare had a booth at the San Mateo Maker Faire, and I was sorely tempted to purchase a Beagle embedded starter kit. Perhaps later...)
The BeagleBoard site is a great resource and there is not much I can add beyond my personal anecdotes.
What you get for $149 is a anti-static bag containing a small (approx 3 inch x 3 inch) PCB and a microSD card. There are no cables supplied.
You can get a quick test of the board w/a RS-232, a USB cable and your trusty PC.
First, install the microSD card which came w/the BeagleBoard.
Second, the USB cable provides power, use the OTG ("On The Go") connector on the BeagleBoard. You should immediately see some LED's light up.
Third, connect the RS-232 cable between the BeagleBoard and your PC. I am using Ubuntu 10.04 LTS w/MiniCom as a console. This step might take some experimentation, but it is working for me at 115200 8N1.
There are two buttons on the PCB next to the USB sockets. (There is a BeagleBoard manual available as a PDF which illustrates the components). Press "reset" to stimulate console activity.
Here is a screen shot of the TI X-Loader starting to boot...Now that you have at least some interaction w/the BeagleBoard you can start adding additional devices.
I purchased a HDMI cable in order to connect an old monitor to my BeagleBoard. I also use USB keyboard and USB mouse to interact w/the BeagleBoard.
The BeagleBoard readily supports a variety of platforms, right now I am most interested in TI arowboat Android distribution (I expect to describe building arowboat in my next post).
Friday, March 12, 2010
Java Date Math
There are 86400 seconds in 24 hours. A typical solution might be to convert the days to seconds since the epoch, then subtract them yielding the difference between days in seconds, then divide by 86400 to obtain the days.
This algorithm fails because not all days are of the same duration.
Julian dates offer a solution. Each day has a unique integer value and caculating the difference is a simple matter of subtraction. Astronomers also use a "julian date" - which represents time and date as a float. (Note: if an integer julian date triggers your Aspergers, please spare me the email. I know the difference).
Example Gregorian/Julian routines are available in Numerical Recipes. Here is a Java class which performs the same functions.
To calculate the days between two dates:
DateMath dm = new DateMath();
int jd1 = dm.gregorianToJulian(calendar1);
int jd2 = dm.gregorianToJulian(calendar2);
int delta = jd1-jd2;
Saturday, January 23, 2010
Junk Or Repair?
The power supplies on these monitors do not age gracefully, and I was a little irritated when both of my VL-1919 started to flake out within weeks of each other.
To my surprise, there is someone who repairs these power supplies: go to eBay and search for "resurrectek" - they do a variety of repairs for a fixed rate.
For $29 each I now have two happy monitors instead of $100+ for two new tubes plus the ewaste from my old displays.
Back to work...
Sunday, January 17, 2010
Mellow Panda and the BC-780-XLT
The 780 is too old for USB and uses a DB-9, which might be a problem since many new computers only have USB ports. I have had good luck w/the TRENDnet TU-S9 USB to RS-232 converter (also works w/my Icom PCR-1000). If you are connecting your 780 to Mellow Panda via USB, you can get the port assignment via dmesg (it will look something like "/dev/ttyUSB1"). This is the value you want for pandad.conf
On the 780 itself, jack up the serial speed to 19.2K (all the way up). This also needs to be reflected in pandad.conf
Bearcat commands follow a request/response cycle in that the computer issues a command/request and the radio responds. All commands/requests will generate a response from the radio like "OK" or "ERR" or a request specific response. If you issue a command using Mellow Panda and do not see a response, there is a connectivity problem.
Note: the 780 will not respond to remote commands if the menu tree is displayed. Press the "menu" key to back out. Of course the RMT flag should also be visible on the display.
There are several sources of information on the Bearcat Uniden command set. Most of the commands make sense, especially if you are familiar w/using the radio.
Mellow Panda tries hard to make your radio accessible: you can exercise the radio by running pandad in the foreground or you can use the panda client. For example, the "SI" command will return system information such as the radio model and version.
For pandad in foreground: ./pandad -x panda://device/raw/bc780.a/SI
For panda client: ./panda -r bc780.a -k raw/SI
Response looks like: SI BC780XLT,0000000000,103
The real fun starts when you need to string commands together to complete a task. Not everything you want to do has an explicit command. Happily, Mellow Panda makes it easy to experiment and I will provide examples of common 780 tasks.
Note:several of these examples are already available as bash scripts in the Mellow Panda demo directory.
Store a frequency and then assign a alphanumeric tag (visible in the radio display)
./panda -r radio_name -k "raw/PM005 01220000"
./panda -r radio_name -k "raw/TA C 005 Flight Watch"
Define channel 5 as the priority channel
./panda -r radio_name -k "raw/PC A004"
Monitor a Motorola Type-2/800 trunk system using only the control channel
./panda -r radio_name -k "raw/PM051 08541875"
./panda -r radio_name -k "raw/KEY13H"
./panda -r radio_name -k "raw/TCN B P1"
./panda -r radio_name -k "raw/KEY00"
./panda -r radio_name -k "raw/KEY13"
Monitor a Motorola Type-2/800 trunk system by defining all associated frequencies
./panda -r radio_name -k "raw/KEY11"
./panda -r radio_name -k "raw/KEY02 3"
./panda -r radio_name -k "raw/KEY02 2"
./panda -r radio_name -k "raw/KEY02 1"
./panda -r radio_name -k "raw/KEY02 1"
./panda -r radio_name -k "raw/KEY11"
./panda -r radio_name -k "raw/KEY11"
./panda -r radio_name -k "raw/KEY11"
./panda -r radio_name -k "raw/PM051 08541875"
./panda -r radio_name -k "raw/KEY13H"
--and more frequencies--
./panda -r radio_name -k "raw/SB B"
./panda -r radio_name -k "raw/KEY00"
./panda -r radio_name -k "raw/KEY13"
Saturday, January 16, 2010
Project Panda
Mellow Panda is a collection of applications which control radio receivers ("scanners") from LINUX via their RS-232/USB interfaces.
Mellow Panda deploys as a daemon (pandad) and specialized clients. pandad is a generic gateway which exposes a TCP stream socket to accept client requests which are relayed to the radios via RS-232/USB. The response returns via the reciprocal path.
pandad is generic and does not interpret the radio commands. Each radio model potentially has a different command set and this specialized knowledge is provided by the client. pandad can simultaneously support multiple radios, each a different model. I routinely drive a Icom PCR-1000, BC-780-XLT and a PRO-2052 all using the same instance of pandad (but each w/a dedicated client).
A twist to this problem is that many models have an asynchronous messaging capability, i.e. the Uniden Bearcat radios will send a message when squelch breaks or trunk id. The Icom PCR-1000 family has a "bandscan" feature which can be used to produce a spectral graph. Each active radio has a UDP socket which broadcasts all traffic generated by the radio. Clients can issue requests via the TCP socket and accept the response either via TCP or get all messages via UDP.
The panda protocol is a simple ASCII string. Even a telnet client is sufficient (I used a telnet client in the early stages for testing).
Communication via a socket has all the usual benefits, sharing a scarce resource and flexibility. Clients can be implemented using any platform or language which is convenient. The pandad distribution has a command line client implemented in C which is useful for development/test and simple scripts.
Other clients will follow as I get them ready to share. pandapy (python) is almost ready along w/pandaj (Java Swing) and pandalet (Tomcat Servlet). You are welcome to contribute additional clients to the project.
Mellow Panda is part of Project Mellow which I am releasing as time permits.



