This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information

Time tracking in Action

If you read previous articles about my experiments with JavaFX and embedded hardware you might be interested in the result.

SIB Visions card

SIB Visions card

  This is the NFC card reader and one of our user cards.

Hardware and case

Hardware and case

  This is the whole system with a nice looking case. This case is hand-made and you can't buy it in a store :)

Beagleboard-xm

Beagleboard-xm

  The back shows the Beagleboard.

Finally, a short video because the solution needs sound to be cool:


Time tracking in action


There are 3 different sounds: OK, OK + completed (whistle) and ERROR. If you hear OK, it means "come in", OK + completed means "Go out". An ERROR means "unknown card".

Have fun.

Pre-built Angstrom image for Beagleboard-xm

Angstrom kernel version: 3.2.28

The archive is available here and contains

  • MLO
  • u-boot.img
  • systemd-GNOME-image
  • modules

The archive contains fresh files but includes the mac address patch.

Check installation instructions.

Source Code modification with Eclipse AST

We heavily use Eclipse AST and JDT. The problem with both APIs is that you don't find a lot of documentation or examples. The best documentation was and is the source code. It shows all secrets.

Last friday at Eclipse DemoCamp Vienna, I discussed with CodeCop about AST and JDT and the missing documentation :)

I wrote a simple class for class/superclass/package/javadoc manipulation some months ago. It is included in VisionX and is not free, but no matter. Here is the source code:

/*
(c) SIB Visions 2012

http://www.sibvisions.com
*/
public class SimpleSourceFile {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Class members
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /** the source parser. */
    private ASTParser parser;

    /** the parsed tokens of the source file. */
    private CompilationUnit cuSource;

    /** the original document. */
    private Document docOrig;

    /** the class type info. */
    private TypeDeclaration tdClass;

    /** the javadoc of the class. */
    private JavadocInfo jdoc;

    /** the full qualified name of the class. */
    private String sClassName;

    /** the full qualified name of the super class after loading. */
    private String sLoadedSuperClass;

    /** the full qualified name of the parent class. */
    private String sSuperClass;

    /** whether the source is changed. */
    private boolean bChanged = false;

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Initialization
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * Creates a new instance of <code>SimpleSourceFile</code> without source
     * information.
     */

    public SimpleSourceFile() {
        load("");
    }

    /**
     * Creates a new instance of <code>SimpleSourceFile</code> with a given
     * source file.
     *  
     * @param pFile the java source file
     * @throws IOException if an error occurs during reading the file
     */

    public SimpleSourceFile(File pFile) throws IOException {
        if (pFile.exists()) {
            JavaSource source = JavaSource.get(pFile.getAbsolutePath());

            if (source != null) {
                synchronized(source) {
                    load(new String(FileUtil.getContent(pFile)));
                }
            } else {
                load(new String(FileUtil.getContent(pFile)));
            }
        } else {
            load("");
        }
    }

    /**
     * Creates a new instance of <code>SimpleSourceFile</code> with a given
     * source stream. The stream is closed after reading
     *  
     * @param pStream the java source stream
     * @throws IOException if an error occurs during reading the stream
     */

    public SimpleSourceFile(InputStream pStream) throws IOException {
        this(pStream, true);
    }

    /**
     * Creates a new instance of <code>SimpleSourceFile</code> with a given
     * source stream.
     *  
     * @param pStream the java source stream
     * @param pAutoClose whether the stream should be closed after reading
     * @throws IOException if an error occurs during reading the stream
     */

    public SimpleSourceFile(InputStream pStream, boolean pAutoClose) throws IOException {
        load(new String(FileUtil.getContent(pStream, pAutoClose)));
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // User-defined methods
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    /**
     * Loads the properties from the source file.
     *
     * @param pSource the java source
     */
@SuppressWarnings({
        "unchecked", "rawtypes"
    })
    private void load(String pSource) {
        jdoc = new JavadocInfo();

        if (parser == null) {
            parser = ASTUtil.createParser();
        }

        parser.setSource(pSource.toCharArray());

        cuSource = (CompilationUnit) parser.createAST(null);

        if (docOrig != null) {
            docOrig.set(pSource);
        } else {
            docOrig = new Document(pSource);
        }

        //we support only one class per file
        List types = cuSource.types();

        if (types.size() > 0 && types.get(0) instanceof TypeDeclaration) {
            tdClass = (TypeDeclaration) types.get(0);

            String sPackage;

            //we have to use the current package, if set
            if (cuSource.getPackage() != null) {
                sPackage = cuSource.getPackage().getName().getFullyQualifiedName() + ".";
            } else {
                sPackage = "";
            }

            sClassName = sPackage + tdClass.getName().getFullyQualifiedName();

            if (tdClass.getSuperclassType() != null) {
                String sSuperName = ((SimpleType)(tdClass.getSuperclassType())).getName().getFullyQualifiedName();

                if (sSuperName.indexOf('.') > 0) {
                    sSuperClass = sSuperName;
                } else {
                    //try to detect the full qualified name of the super class through the imports

                    List < ImportDeclaration > liImports = cuSource.imports();

                    boolean bFound = false;

                    if (liImports != null) {
                        String sTempName = "." + sSuperName;
                        String sImport;

                        for (int i = 0, anz = liImports.size(); !bFound && i < anz; i++) {
                            sImport = liImports.get(i).getName().getFullyQualifiedName();
                            if (sImport.endsWith(sTempName)) {
                                bFound = true;

                                sSuperClass = liImports.get(i).getName().getFullyQualifiedName();
                            }
                        }
                    }

                    if (!bFound) {
                        //we use the current package
                       sSuperClass = sPackage + sSuperName;
                    }
                }

                //cache for optimizing imports in apply
                sLoadedSuperClass = sSuperClass;
            }

            jdoc = new JavadocInfo(tdClass.getJavadoc(), pSource);
        } else {
            tdClass = null;
        }

        //currently loaded file can not be changed!
        bChanged = false;
    }

    /**
     * Sets the comment for the class.
     *
     * @param pText the comment
     */

    public void setClassComment(String pText) {
        bChanged |= !CommonUtil.equals(pText, jdoc.getDescription());

        jdoc.setDescription(pText);
    }

    /**
     * Gets the comment for the class.
     *
     * @return the comment
     */

    public String getClassComment() {
        return jdoc.getDescription();
    }

    /**
     * Sets the full qualified name of the class.
     *
     * @param pName the class name
     */

    public void setClassName(String pName) {
        if (pName == null) {
            throw new IllegalArgumentException("It is not allowed to remove the class definition (class name = null)!");
        }

        bChanged |= !CommonUtil.equals(pName, sClassName);

        sClassName = pName;
    }

    /**
     * Gets the full qualified name of the class.
     *
     * @return the class name
     */

    public String getClassName() {
        return sClassName;
    }

    /**
     * Sets the full qualified class name of the parent class.
     *
     * @param pSuperClass the full qualified class name of the parent class
     */

    public void setSuperClass(String pSuperClass) {
        bChanged |= !CommonUtil.equals(pSuperClass, sSuperClass);

        sSuperClass = pSuperClass;
    }

    /**
     * Gets the full qualified class name of the parent class.
     *
     * @return the class name
     */

    public String getSuperClass() {
        return sSuperClass;
    }

    /**
     * Replaces the relevant information with the specific values
     * and saves the source file.
     *
     * @throws Exception if the source modification fails
     */
@SuppressWarnings("unchecked")
    protected void apply() throws Exception {
        if (!bChanged) {
            return;
        }

        AST ast = cuSource.getAST();

        ASTRewrite arw = ASTRewrite.create(ast);

        //----------------------------------------------------
        // Change Package, Classname and Constructor
        //----------------------------------------------------

        String sCurrentPackage;

        //set package
        String[] sClassInfo = ClassUtil.splitName(sClassName);

        if (sClassInfo[0] != null) {
            if (cuSource.getPackage() != null) {
                arw.set(cuSource.getPackage(), PackageDeclaration.NAME_PROPERTY, ast.newName(sClassInfo[0]), null);

                sCurrentPackage = sClassInfo[0];
            } else {
                PackageDeclaration pdcl = ast.newPackageDeclaration();
                pdcl.setName(ast.newName(sClassInfo[0]));

                arw.set(cuSource, CompilationUnit.PACKAGE_PROPERTY, pdcl, null);

                sCurrentPackage = sClassInfo[0];
            }
        } else {
            arw.set(cuSource, CompilationUnit.PACKAGE_PROPERTY, null, null);

            sCurrentPackage = null;
        }

        if (tdClass == null) {
            //new class
            tdClass = ast.newTypeDeclaration();
            tdClass.setName(ast.newSimpleName(sClassInfo[1]));

            ListRewrite lrwImp = arw.getListRewrite(cuSource, CompilationUnit.TYPES_PROPERTY);
            lrwImp.insertFirst(tdClass, null);
        } else {
            //change class name
            SimpleName snClass = ast.newSimpleName(sClassInfo[1]);

            arw.set(tdClass, TypeDeclaration.NAME_PROPERTY, snClass, null);

            //chage constructors

            MethodDeclaration[] methods = tdClass.getMethods();

            if (methods != null) {
                for (MethodDeclaration method: methods) {
                    if (method.isConstructor()) {
                        arw.set(method, MethodDeclaration.NAME_PROPERTY, snClass, null);
                    }
                }
            }
        }

        //change Superclass

        List < ImportDeclaration > liImports = cuSource.imports();

        //remove "old" import
       if (sLoadedSuperClass != null) {
            if (liImports != null) {
                boolean bFound = false;

                for (int i = liImports.size() - 1; !bFound && i >= 0; i--) {
                    if (sLoadedSuperClass.equals(liImports.get(i).getName().getFullyQualifiedName())) {
                        bFound = true;

                        arw.remove(liImports.get(i), null);

                        liImports.remove(i);
                    }
                }
            }
        }

        if (sSuperClass == null) {
            arw.set(tdClass, TypeDeclaration.SUPERCLASS_TYPE_PROPERTY, null, null);
        } else {
            //check new super class import
            String[] sSuperClassInfo = ClassUtil.splitName(sSuperClass);

            if (sSuperClassInfo[0] != null) {
                boolean bFound = false;

                if (sCurrentPackage != null) {
                    bFound = sSuperClassInfo[0].equals(sCurrentPackage);
                }

                if (!bFound) {
                    if (liImports != null) {
                        for (int i = 0, anz = liImports.size(); !bFound && i < anz; i++) {
                            bFound = sSuperClass.equals(liImports.get(i).getName().getFullyQualifiedName());
                        }
                    }
                }

                //class not already importet -> add import
               if (!bFound) {
                    ImportDeclaration idSuper = ast.newImportDeclaration();
                    idSuper.setName(ast.newName(sSuperClass));

                    ListRewrite lrwImp = arw.getListRewrite(cuSource, CompilationUnit.IMPORTS_PROPERTY);
                    lrwImp.insertLast(idSuper, null);
                }
            }

            arw.set(tdClass, TypeDeclaration.SUPERCLASS_TYPE_PROPERTY, ast.newSimpleType(ast.newName(sSuperClassInfo[1])), null);
        }

        //----------------------------------------------------
        // Change Javadoc of the class
        //----------------------------------------------------

        if ("".equals(jdoc.toString())) {
            //no javadoc
            arw.set(tdClass, TypeDeclaration.JAVADOC_PROPERTY, null, null);
        } else {
            arw.set(tdClass, TypeDeclaration.JAVADOC_PROPERTY, jdoc.createJavadoc(tdClass.getAST()), null);
        }

        //----------------------------------------------------
        // Modify AST
        //----------------------------------------------------

        TextEdit edit = arw.rewriteAST(docOrig, null);
        edit.apply(docOrig);

        ASTUtil.applyFormat(docOrig);

        //simple reparse (important for the comments)
        parser.setSource(docOrig.get().toCharArray());

        cuSource = (CompilationUnit) parser.createAST(null);

        //----------------------------------------------------
        // Modify comments
        //----------------------------------------------------

        //Class comment after the last bracket

        int iCorr = 0;

        List < Comment > liComments = cuSource.getCommentList();

        if (liComments != null) {
            String sOldName = tdClass.getName().getFullyQualifiedName();
            String sText;

            for (Comment com: liComments) {
                sText = docOrig.get(com.getStartPosition() - iCorr, com.getLength());

                int iPos = 0;
                int iClassLen = sClassInfo[1].length();
                int iLenDiff = sOldName.length() - iClassLen;

                int iNextPos;

                CharacterType ctypePrev;
                CharacterType ctypeNext;

                while ((iPos = sText.indexOf(sOldName, iPos)) >= 0) {
                    if (iPos > 0) {
                        ctypePrev = StringUtil.getCharacterType("" + sText.charAt(iPos - 1));
                    } else {
                        ctypePrev = CharacterType.OnlySpecial;
                    }

                    iNextPos = iPos + sOldName.length();

                    if (iNextPos <= sText.length() - 1) {
                        ctypeNext = StringUtil.getCharacterType("" + sText.charAt(iNextPos));
                    } else {
                        ctypeNext = CharacterType.OnlySpecial;
                    }

                    //don't replace word parts e.g. ContactsWorkScreen should not be replaced with Contacts
                    if ((ctypePrev == CharacterType.OnlySpecial || ctypePrev == CharacterType.OnlyWhitespace) && (ctypeNext == CharacterType.OnlySpecial || ctypeNext == CharacterType.OnlyWhitespace)) {
                        docOrig.replace(com.getStartPosition() + iPos - iCorr, sOldName.length(), sClassInfo[1]);

                        iCorr += iLenDiff;
                        iPos += iClassLen;
                    } else {
                        iPos = iNextPos;
                    }
                }
            }
        }

        //----------------------------------------------------
        // Update
        //----------------------------------------------------

        load(docOrig.get());

        bChanged = false;
    }

    /**
     * Stores the changed source into the given file.
     *
     * @param pFile the target file for the source
     * @throws Exception if an error occurs during saving
     */

    public void saveAs(File pFile) throws Exception {
        apply();

        JavaSource source = JavaSource.get(pFile.getAbsolutePath());

        if (source != null) {
            synchronized(source) {
                FileUtil.save(pFile, docOrig.get().getBytes());

                source.reload();
            }
        } else {
            FileUtil.save(pFile, docOrig.get().getBytes());
        }
    }

    /**
     * Stores the changed source into the given stream.
     *
     * @param pStream the target stream for the source
     * @throws Exception if an error occurs during saving
     */

    public void saveAs(OutputStream pStream) throws Exception {
        apply();

        pStream.write(docOrig.get().getBytes());
        pStream.flush();
    }

    /**
     * Returns the modified source code.
     *  
     * @return the modified source code
     * @throws Exception if the source modification fails
     */

    protected String getSource() throws Exception {
        apply();

        return docOrig.get();
    }

} // SimpleSourceFile

Important imports:

import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jface.text.Document;
import org.eclipse.text.edits.TextEdit;

The class contains references to other classes like CommonUtil, FileUtil, StringUtil. These classes are included in JVx. There are two classes (JavaSource, ClassUtil) that are not free, but you should find out how it works without these classes.

If you'll set a new superclass, the superclass and its import, if available, will be updated. If you'll set a new classname, all constructors will be updated.

And once again: The best documentation is the source code :)

Java sound with Beagleboard

I tried to play some wav files with Java (javax.sound.sampled) on my Beagleboard. It worked without Exceptions but I didn't hear the sound. The hardware was not the problem...

By default, sound output was set to muted :(

I solved the problem with

opkg install alsa-utils
//optional for tests
opkg install mplayer

Use alsamixer for the configuration:

Alsamixer

Alsamixer

  • Set Headset volumne
  • Toggle mute for HeadsetL Mixer AudioL1 and HeadsetR Mixer Audio R1 (Press m key)
  • Check volume for DAC2 Analog

Test:

mplayer sound.wav

JavaFX TimeTracking with Beagleboard-xm

My first JavaFX "embedded" application is ready.

The application is a time tracking tool. It uses RFID cards for user authentication and sends time entries to a remote Redmine project management tool. It works great.... now.

Before I continue, a screenshot of the current application taken via VNC from my Beagleboard:

Time tracking

Time tracking

... I'm not a UI designer ;-)

My first design was a little bit different because I began to develop on my Windows Laptop and used some nice transitions and effects. The first layout was this:

Time tracking (Laptop)

Time tracking (Laptop)

Some problems with this design - on my Beagleboard - were bad peformance of TableView updates, slow transitions, problems with innershadow effect:

Time tracking (1:1)

Time tracking (1:1)

I don't know why the shadow effect worked for the table border, but the UI was not usable. It was too slow and the clock was not shown because of other problems described from Gerrit Grunwald.

After short frustration, I began to tweak:

  • Replaced TableView with ListView
  • Replaced Clock from JFXtras with a styled Label
  • Smaller buttons for better performance
  • Specific stylesheets for embedded environment

It was not a lot of work to change the application, but I thought the performance would be better. It's very important to reduce effects and transitions for applications that should run on an embedded device like Beagleboard-xm. It's a little bit like developing mobile applications (touch instead of mouse, screen size, performance, ...).

The current JavaFX embedded runtime is not perfect and the performance could be better, but if you are careful, you can create very useful applications.

And finally a picture of my hardware:

Hardware

Hardware

NFC/RFID for Beagleboard xm with Java

NFC reader   Based on my experience with JavaFX, Beagleboard xm, RxTx and GSM devices, I started my new research project: A simple and cheap time tracking solution.

There are hundreds of tools and hardware solutions available for time tracking, but hardware solutions are expensive and tools without hardware are not practically.

The ideal solution would use a Raspberry Pi and a RFID reader together with a simple JavaFX application. Total costs are about EUR 50,- for the hardware and software should be free :) If you need a case for your hardware and a lcd display, it would be more expensive but EUR 150,- should be the limit for a brilliant system.

I ordered my Raspberry Pi some weeks ago but it is still not available and hopefully it will arrive before Xmas? My fallback board is the Beagleboard xm.

The big question was the RFID reader/writer. Wich devices were supported, and were there any Java APIs for the communication? After a short google search I found a lot of recommendations for RFID breakout board, but it was not too cheap and I don't have an Arduino board and don't want one. I found some other boards but I decided to use PN532 NFC RFID module kit. It is still expensive but seems to be fair.

Why this RFID board?

Firstly, because of a mistake. I saw a screenshot of an example application and the frame icon was a Java icon... So I thought the application was written with Java and there must be a Java API. Not really!
The whole kit was available as developer version with a lot of accessoires like two cards, one dongle, additional cables, ....

After reading the documentation and provided information it was clear that the board was perfect for Arduino boards and the software was written in C/C++, but it was still not too expensive.

But no risk, no fun - ordered.

The delivery time was ok and after unpacking I thought it would be easy to test the included RFID cards - Plug and Play? The truth: No way.

First problem

Included cables were built for Arduino board. I kew that the connection could be a problem and ordered an USB TTL module together with the RFID baord.

The USB module has a connector with 4 pins (GND, VCC, RX, TX) and the RFID board has 4 pins (GND, VDD, RX/SDA, TX/SDL). It was not a big problem to connect the right pins after it was clear that the included standard cable will not work.

Solved.

Second problem

The RFID reader supports 3 different modes (HSU, I2C, SPI). The default mode was I2C because of the Arduino support. But in order to support my Beagleboard, HSU was needed. The board has no jumpers and you must solder a little bit to, but with the right tools it's easy.

Solved.

Third problem

No Arduino board, no chance to test the RFID board? Not really.
There is a great open source project with the name libnfc. It has read/write and diagnostic tools and runs on Linux and should run on Windows too. I compiled libnfc on my Ubuntu VM and had the problem that the nfc-list tool reported a timeout after connecting - but the RFID board was found.
Hm...

It was easy to compile libnfc on Ubuntu - after installing some dependencies - so I thought it should be easy to compile it on my Beagleboard xm? It was a bad idea and I spent some extra hours... but it worked.

How? I can't remember every single step, but try following:

Dependencies:

opkg install cc
opkg install gcc-dev
opkg install task-native-sdk
opkg install libusb-1.0-0 libusb-1.0-dev
opkg install libusb-0.1-4 libusb-0.1-dev
opkg install udev-dev
opkg install autoconf

You need following source packages:

pcsc-lite.1.7.0
libnfc-1.5.1

(Newer versions or different version combinations did not work because libraries were not available for my distribution. But this versions are good enough for tests.)

Start with pcsclite:

./bootstrap
./configure --disable-libhal
make install

Copy /usr/local/include/PCSC directory to /usr/include and /usr/local/lib/pkgconfig/libpcsclite.pc to /usr/lib/pkconfig/

Next, compile libnfc:

./configure --with-drivers=pn532_uart,pn53x_usb --enable-serial-autoprobe --enable-debug
make install

If you get the exception similar to ln: symbol not found -lusb, create a symbolic link:

cd /lib
ln -s libusb-0.1.so.4.4.4 libusb.so

Test, in your libnfc directory:

cd utils
./nfc-list
cd ../examples
# hold a card over the RFID board
./nfc-poll

My output:

# ./nfc-list
Connected to NFC device: PN532 (/dev/ttyUSB0) - PN532 v1.6 (0x07)

# ./nfc-poll
NFC device will poll during 30000 ms (20 pollings of 300 ms for 5 modulations)
ISO/IEC 14443A (106 kbps) target:
    ATQA (SENS_RES): 00  04
       UID (NFCID1): 1b  21  4b  e0
      SAK (SEL_RES): 08

Now you know that your hardware is supported and works with CPP libraries. But what about Java?

I found NFC Tools but the online demo did not work with my reader. Another solution was Open-NFC.

Both solutions were feature rich but horrible because the first one did not compile and maven could not resolve all dependencies. The other solution was too complex for my use case. Why the hell are most libraries so complex. Nobody can handle big libraries.

My use-case was simple: I need a small Java library that can be bundled with my application and it should work on Linux, Windows and optionally on MacOS. It should connect to my Reader via RS232. I stopped evaluating libraries and decided to create my own "simple" library for my use-case.

The topic (NFC, RFID, smart cards) is really complex but I had a PDF that described how read/write basically should work. And I had a reference implementation in cpp. After some problems and night hacking, the problem was solved and I had my library that simply worked (only 2 source files and ~ 1000 LoC).

My first application only reads content from cards that are over the reader. Here is the output:

SAM = true
LOOP PAUSE
UUID length: 4
UUID: 1b 21 4b e0
Authentication receive: 00 FF 03 FD D5 41 00 EA
Authentication successful
Read block successfully:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Read block successfully:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Read block successfully:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Read block successfully:
00 00 00 00 00 00 FF 07 80 69 FF FF FF FF FF FF

And it supports writing to the card, but don't overwrite the 4th block because it contains security settings. If you overwrite the 4th block with wrong values, the whole sector is unusable.

What'll come next? A JavaFX UI for in/out tracking.
The library itself is free but the source is not yet available because it needs some refactoring and beautifying.

Have fun ;-)

JavaFX development for Beagleboard xm

TableView

TableView

  This is my first application for the board.
It's not rocket science but was already developed :)

This screenshot was captured via VNC. The installation of VNC was very easy:

opkg install angstrom-x11vnc-xinit
killall Xorg

Wait until X was restarted and use a VNC Viewer (e.g. TightVNC) to create a connection to port 5900. More details are available here.

The application was developed with JavaFX 2.2.2, but on the beagleboard (with the current preview version) it is version 8.0.0-ea-b55. If you develop custom controls be careful because SkinBase was moved to javafx.scene.control. If you want develop with an 8.x version, you must download a JDK8 EA version. It works for me but some methods are different in 8.x as in 2.x and of course they removed some deprecated "impl_" methods :)

Another problem with my test application is that I have no scrollbars (see screenshot) and scrolling with my mouse does not work. Sort on header and resize columns is not possible.

Don't expect too much from the current JavaFX support for ARM. It's in a very early phase, but if you create your own "controls" you get a powerful toy.

JavaFX + ScenicView + Javeleon = LiveFX

JavaFx + ScenicView

JavaFx + ScenicView

  If you use JavaFX for development, normally you create applications with a GUI - so far so good. How often do you restart your application to check if a simple code change works? Many times?

If you develop a control, wouldn't it be great to see your code changes immediately?

We use Javeleon for application development together with JVx because it saves a lot of development time and creates live feeling. We thought that Javeleon could also be useful for JavaFX application or control development because application restarts always waste development time.

The integration of Javeleon in your JavaFX application is not difficult, simply add two VM arguments

-Xbootclasspath/p:"D:\temp\JaveleonBoot\default"
-javaagent:D:\libs\javeleon\javeleon.jar

to your launch/run configuration (use your directories). If you use Javeleon for development, it reduces application restarts to a bare minimum, but you should know the limits.

Javeleon reloads classes directly in your VM, but it does not recreate members of your loaded instances and does not call the constructor of your classes again.

You should use a Javeleon reload listener to trigger reload of resource files (css, xml) and call specific methods after reload e.g. call init again.

Imagine you have a GUI with a button and on click you start the creation of a report or execute some business logic, etc. If you change the source code for your button click action or change a method used in the action, your application always invokes the changed "code".

Sounds tricky? It isn't, but live coding is not well known.

If you are interested, simply try it out!

I'm not sure whether the current downloadable Javeleon 3.0 is up-to-date because I found some smaller problems with ScenicView. The problems were already fixed and hopefully released asap.
My working version number: Javeleon version 3.0.1 dev 2012-11-13 (r699).

If you have problems, simply contact the Javeleon team.

BeagleBoard xm and WLAN

I tried to connect an USB WLAN adapter to my board and thought it should work out-of-the-box like my cordless mouse (WLAN c'mon). But that was not the case. The good news is that it already works. The bad news is that it took hours to figure out how it works.

Maybe my requirements are too specific (I don't think so), but the documentation in the Internet about beagleboard stuff is not really good. I found a lot of old articles and most of them did not work or did not work with my software. It took hours to find information snippets for my requirements.
Are a fixed MAC address and WLAN support really too modern?

Anyway, I try to prepare my hardware before I start with development. I'm not totally happy at the moment because my touch screen does not work with JavaFX applications. It's an ELO touchscreen EL1725 connected via USB. I hope that this ticket solves my problem.

Some background information for my WLAN adapter

I read the manual before I bought an adapter and was suprised that only some specific adapters were supported (maybe the documentation is not up-to-date). I chose Trendnet TEW-648UBM Micro. It was not in the list, but I thought it could work...

After I plugged the adapter to my last free USB port I checked dmesg and saw no errors. The adapter was detected and I thought it should work. Typed

ifup wlan0

and got error messages. Checked dmesg and saw an information about missing firmware.

The adapter needs a firmware but it didn't find it on my SD card :( . I asked one of my best friends, google, and got a lot of results but no answer. So I tried

opkg list|grep realtek

because dmesg showed me that my adapter has a realtek chip. I got results, but no firmware. The next try was:

opkg list|grep firmware

I got too many results :) My next attempt:

opkg list|grep 8192cu

I found my firmware and installed it:

opkg install linux-firmware-rtl8192cu

Now the adapter was integrated but it was not configured. My WLAN is secured with a passphrase.
I tried to configure the wlan0 interface via /etc/network/interfaces and /etc/wpa_supplicant.conf but without success. So I asked google again and found some "useful" articles:

Beaglebone WiFi
connman
Beagleboard WiFi USB dongle

The second article was not bad but did not work. The last one was better but it did not work as described :)
Mabe I did not understand something that was written between the lines.

What I did:

opkg install connman-tests connman-tools

I did nothing with /etc/network/interfaces because the connman has its own configuration! So I created a file with the name /var/lib/connman/wifi.config. To find out the SSID the following command was useful:

/usr/lib/connman/test/test-connman services
/usr/lib/connman/test/get-services

Set [WiFi] Enabled = true in /var/lib/connman/settings.

My wifi.config

[global]
Name = NetworkName
Description = Home WLAN

[service_networkname]
Type = wifi
ssid = 123456789
Passphrase = mypassword

Be sure that your Name is equal to the name in the service section. The ssid was taken from output of

/usr/lib/connman/test/test-connman services

mynetwork {wifi_<macaddr>_<sid>_managed_psk}

My /etc/network/interfaces

# The loopback interface
auto lo
iface lo inet loopback

# Wireless interfaces
#iface wlan0 inet dhcp
#       wireless_mode managed
#       wireless_essid any
#       wpa-driver wext
#       wpa-conf /etc/wpa_supplicant.conf

iface atml0 inet dhcp

# Wired or wireless interfaces
auto eth0
iface eth0 inet dhcp
iface eth1 inet dhcp

After rebooting the board the connection was established automatically. If you have problems, use gnome to check your network settings or try to establish a connection with your network.

Sometimes during startup it was not possible to load the firmware for the adapter, but normally it worked without problems. If you have problems, simply use dmesg and journalctl to find error details.

BeagleBoard xm fixed MAC address

The BeagleBoard xm has no fixed MAC address. During startup, a random MAC address is created and used. This is a problem if you wanna use it in your local LAN with Firewalls. It also is a problem to assign a fixed IP to the board, because the MAC address changes.

I searched and tried a lot of things to set a fixed MAC address. All hints didn't work:

Set an udev rule to assign a fixed MAC address
or configure eth0 with a fixed hwaddress

Well, to get a fixed MAC address I had to patch my image :( but how? I built my Angstrom image from scratch but all sources were downloaded as archives and bitbaker extracts/compiles the sources automatically. I found no documentation for manuall patching. And I didn't know what I should patch.

So I started researching. The network module was smsc95xx but I had no idea which version was used for the kernel. I had to find out the version before I could search a patch...

I knew that the file was stored in a directory with the name drivers/net/usb. All source archives are available in <dir>/setup-scripts/sources/downloads. An archive search should find the right archive but I had to find the build file for the kernel...

After some hours googling I found that <dir>/setup-scripts/sources/meta-ti/recipes-kernel/linux contains kernel buildfiles, but more than one! I figured out that linux-mainline_3.2.bb was the right buildfile. I checked the content and found the link to the source archive (online). After reading the source file I knew that the following patch should work: MAC address patch. Some details about this patch are available here.

I had a patch file and knew the buildfile but how should I apply the patch? It was not too complex because the buildfile already contained a list of patchfiles. I added my patchfile to the list and copied it to the same directory where all other patchfiles were stored.

The next step was the rebuild of my image... Sounds simple but how? It was simple with following commands:

bitbake -c clean virtual/kernel
bitbake virtual/kernel
bitbake systemd-gnome-image

Some hours later, I had a new kernel and recreated my SD card. To set a MAC address, I changed uEnv.txt:

optargs="consoleblank=0 smsc95xx.macaddr=xx:xx:xx:xx:xx:xx"

It works!