Thursday, June 13, 2013

Beaglebone Black, ANT+, and Garmin FR70



My BBB is running Debian. Your mileage may vary if your BBB is running the stock Ångström.

Just successfully extracted data from my Garmin FR70 to BBB via a Garmin ANT+ USB stick. The steps are quite simple once you figured what software you need.

Note that different version of Garmin products use different protocol to communicate. The program Garmin-Forerunner-610-Extractor supports the followings:

 - Garmin Forerunner 60
 - Garmin Forerunner 405CX
 - Garmin Forerunner 310XT
 - Garmin Forerunner 610
 - Garmin Forerunner 910XT
 - Garmin FR70
 - Garmin Swim


Basically you just follow the instructions on the page. To sync with your BBB the first time, remember to turn on the Pairing function on you device. On my FR70, it is under Settings > System > Computer > Pairing.

For other devices

If you are using other devices that are not supported by Garmin-Forerunner-610-Extractor, go check out garmin-ant-downloader (this can be installed via apt-get). If you are using garmin-ant-downloader, your may need to create the USB serial device manually on your BBB. After you plugged in the ANT+ USB stick, use the lsusb command to list the device:
debian@debian-armhf:~/garmin$ lsusb
Bus 001 Device 002: ID 058f:6254 Alcor Micro Corp. USB Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 154b:0062 PNY
Bus 001 Device 008: ID 0fcf:1008 Dynastream Innovations, Inc.


The last device is my ANT+ stick. The garmin-ant-downloader needs to access the stick via a serial device. By default, it is the /dev/ttyUSB0. If it is not created automatically, use the following command:
sudo modprobe usbserial vendor=0x0fcf product=0x1008


Where 0x0fcf and 0x1008 are the code from the lsusb command result. Use the values that correspond to the output of your stick.

Using Oracle JDK 8 for ARM Early Access VM in OpenJDK under BeagleBoneBlack

Note that Oracle 8 JDK official release for ARM is out.  There is no need to follow the steps below.  Also, if you want to install it via apt-get, you may refer to this link.

Following similar instructions for Raspberry Pi, here are the instructions on how to boost the Java performance on BeagleBone Black by using Oracle's JDK 8 Early Access HotSpot with OpenJDK.

Note that I am using Debian on my BBB

Install openjdk
/mnt/usb/debian/test$ sudo apt-get install openjdk-7-jdk


Optional. Download a benchmark program. Save it as fastaredux.java and test the performance of the stock OpenJDK.
/mnt/usb/debian/test$ java -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.7) (7u3-2.1.7-1)
OpenJDK Zero VM (build 22.0-b10, mixed mode)
/mnt/usb/debian/test$ javac fastaredux.java
/mnt/usb/debian/test$ time java -XX:+TieredCompilation -XX:+AggressiveOpts fastaredux 25000000 > /dev/null 2>&1

real    1m40.294s
user    1m40.047s
sys     0m0.180s


Download the JDK 8 for ARM Early Access package from Oracle web site.

Following the instructions, extract the hotspot VM and add it to OpenJDK

/mnt/usb/debian/test$ tar --extract --verbose --file=jdk-8-ea-b36e-linux-arm-hflt-29_nov_2012.tar.gz jdk1.8.0/jre/lib/arm/client
jdk1.8.0/jre/lib/arm/client/
jdk1.8.0/jre/lib/arm/client/Xusage.txt
jdk1.8.0/jre/lib/arm/client/libjvm.so
jdk1.8.0/jre/lib/arm/client/libjsig.so
/mnt/usb/debian/test$ sudo mv jdk1.8.0/jre/lib/arm/client /usr/lib/jvm/java-7-openjdk-armhf/jre/lib/arm/oracle
/mnt/usb/debian/test$ sudo chown -R root:root /usr/lib/jvm/java-7-openjdk-armhf/jre/lib/arm/oracle


Make the HotSpot VM as our default VM. Edit the file /etc/java-7-openjdk/jvm-armhf.cfg and add -oracle KNOWN as the first parameter. (or, as stated in the instructions, use the sed command to add it to the first line of the file)
-oracle KNOWN
-server KNOWN
-client IGNORE
-hotspot ERROR
-classic WARN
-native ERROR
-green ERROR
-zero ALIASED_TO -server
-cacao KNOWN
-zero ERROR
-shark ERROR
-jamvm KNOWN


Note the last line of the output. Now it changed to use HotSpot VM
/mnt/usb/debian/test$ java -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.7) (7u3-2.1.7-1)
Java HotSpot(TM) Client VM (build 25.0-b04, mixed mode)


Result of running the same benchmark. The performance almost tripled.
/mnt/usb/debian/test$ time java -XX:+TieredCompilation -XX:+AggressiveOpts fastaredux 25000000 > /dev/null 2>&1

real    0m35.102s
user    0m34.906s
sys     0m0.166s


Note that using the -server option on the HotSpot VM seems to slow down the performance though.

Using the -server option will actually switch back to use the default OpenJDK Zero VM
$ java -server -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.7) (7u3-2.1.7-1)
OpenJDK Zero VM (build 22.0-b10, mixed mode)
$ java -version
java version "1.7.0_03"
OpenJDK Runtime Environment (IcedTea7 2.1.7) (7u3-2.1.7-1)
Java HotSpot(TM) Client VM (build 25.0-b04, mixed mode)


You can also alias -server option to use HotSpot VM:
-oracle KNOWN
-server ALIASED_TO -oracle
#-server KNOWN
-client IGNORE
-hotspot ERROR
-classic WARN
-native ERROR
-green ERROR
-zero ALIASED_TO -server
-cacao ERROR
-zero ERROR
-shark ERROR
-jamvm KNOWN


PS. Here is the before vs after result on my Raspberry Pi.

Before:
pi@raspberrypi ~/test $ time java -XX:+TieredCompilation -XX:+AggressiveOpts fastaredux 25000000 > /dev/null 2>&1

real    8m4.807s
user    7m56.410s
sys     0m1.880s


After:
pi@raspberrypi ~/test $ time java -XX:+TieredCompilation -XX:+AggressiveOpts fastaredux 25000000 > /dev/null 2>&1

real    0m44.109s
user    0m43.160s
sys     0m0.320s

Wednesday, June 12, 2013

OpenCV facial recognition with BeagleBone Black

After flashing my BBB with Debian, I proceed to install OpenCV and the required Python packages. Using the sample code from here, I could get the facial and eye recognition demo running in a few minutes!

A few points to note:

  • I am running Debian Wheezy on my BBB with the image flashed to eMMC. It is a headless setup. Connect to it from my Windows PC using putty and Xming
  • BBB seems to be having problem when the webcam resolution is too high. The sample code ran OK when the witdth and height set as 320x240 or 352x288
  • For the facial recognition sample, some external XML files are required. You may get them from the OpenCV source code package. Just modify the sample code to point to the correct directory


Monday, June 10, 2013

Ångström...

I think I will give up Ångström and flash Debian Wheezy onto the eMMC of my BeagleBond Black directly...

Sunday, June 9, 2013

Upgrading Ångström on Beaglebone Black

Got my BBB a few weeks ago. My first attempt to update and upgrade the stock Ångström failed. I knew that the in memory /tmp is not big enough for "opkg upgrade" to store the downloaded packages. So I added the "-t" switch to write the temp files to another directory.

However, even the BBB eMMC is not big enough. The upgrade failed and probably corrupted the base. Needed to re-flash the eMMC.

Notes to self: Plug in a USB stick and use it as temp storage for "opkg upgrade" next time!!

Friday, October 12, 2012

Testing Raspberry Pi network speed

Just got another Rapsberry Pi board. This time a 2.0 board (made in the UK!!). Anyway, doing a network (or USB, since the ethernet is via USB) stress test with the following command:

cat /dev/zero | ssh username@remotehost "cat > /dev/null"


That caused 100% CPU loading on the Raspberry Pi. Throughput is about 6MB per second.

Monday, October 1, 2012

Using PMD to scan explicit/implicit casting of BigDecimal to String

At work, a project needed to be upgraded from Java 1.4 to Java 1.6. One of the big issues introduce in Java 1.5 was the behavior change of the toString method of BigDecimal. Under certain situation, it now returns the string in using scientific notation. This change was based on JSR 13. While the change is justifiable, some question if the expert group underestimated the behavioral compatibility impact, especially with programs that interface with databases.

Anyway, one way (the way we used at work) to solve the issue is to use the new method toPlainString. We grep the code to find all the toString method calls and manually change them to toPlainString. But not only this is prone to error, there are many cases that can't be detected. For example, this code doesn't call the toString method explicitly but still affected by the compatibility issue :

String s = "" + bd;


There ought to be a better (and automated) way to scan the source tree. So during a long weekend, I spent a day to play with PMD. Below is a rule that I implemented to do the job. Part of it is based on a sample found in the PMD package.But note that this still CANNOT find all the cases of BigDecimal.toString (more about that later).

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.List;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTFormalParameter;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
import net.sourceforge.pmd.lang.java.ast.ASTType;
import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
import net.sourceforge.pmd.lang.java.ast.ASTName;
import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
import net.sourceforge.pmd.lang.java.symboltable.NameOccurrence;
import net.sourceforge.pmd.util.CollectionUtil;


/**
 *
 */
public class ScanBigDecimal extends AbstractJavaRule {

    /**
     * These are the BigDecimal methods
     */
    private static final Set<String> BIG_DECIMAL_METHODS = CollectionUtil.asSet(new String[] { ".toString" });

    /**
     * These are the classes that the rule can apply to
     */
    private static final Map<String, Set<String>> MAP_CLASSES = new HashMap<String, Set<String>>();
    static {
        MAP_CLASSES.put("java.math.BigDecimal", BIG_DECIMAL_METHODS);
        MAP_CLASSES.put("BigDecimal", BIG_DECIMAL_METHODS);
    }

    @Override
    public Object visit(ASTLocalVariableDeclaration node, Object data) {

        ASTVariableDeclaratorId var = getDeclaration(node);
        if (var == null) {
            return super.visit(node, data);
        }
        String variableName = var.getImage();
        for (NameOccurrence no: var.getUsages()) {
            // FIXME - getUsages will return everything with the same name as the variable,
            // see JUnit test, case 6. Changing to Node below, revisit when getUsages is fixed
            Node sn = no.getLocation();
            Node primaryExpression = sn.jjtGetParent().jjtGetParent();
            Class<? extends Node> parentClass = primaryExpression.jjtGetParent().getClass();

            // see if we are calling the methods we are interested in
            String methodCall = sn.getImage().substring(variableName.length());
            ASTType nodeType = node.getTypeNode();
            if (nodeType != null && MAP_CLASSES.get(nodeType.getTypeImage()).contains(methodCall)) {
                addViolation(data, sn);
            }
        }
        return super.visit(node, data);
    }

    @Override
    public Object visit(ASTAdditiveExpression node, Object data) {

        boolean hasLiteral = true;
        boolean hasClass = false;

        if (node == null) {
            return super.visit(node, data);
        }

        for (int i = 0; i < node.jjtGetNumChildren(); i++) {
            Node child = node.jjtGetChild(i);

            if (child.hasDescendantOfType(ASTLiteral.class)) {
                hasLiteral = true;
            } else {
                ASTName nameNode = (ASTName)child.getFirstDescendantOfType(ASTName.class);

                if (nameNode != null && findDeclaration(node, nameNode.getImage()) != null) {
                    hasClass = true;
                }
            }

            if (hasLiteral && hasClass) {
                addViolation(data, node);
                break;
            }
        }

        return super.visit(node, data);
    }


    /**
     * This method checks the variable declaration if it is on a class we care
     * about. If it is, it returns the DeclaratorId
     *
     * @param node
     *            The ASTLocalVariableDeclaration which is a problem
     * @return ASTVariableDeclaratorId
     */
    private ASTVariableDeclaratorId getDeclaration(ASTLocalVariableDeclaration node) {
        ASTType type = node.getTypeNode();
        if (MAP_CLASSES.keySet().contains(type.getTypeImage())) {
            return node.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
        }
        return null;
    }

    private ASTVariableDeclaratorId findDeclaration(Node node, String varName) {
        if (node == null || varName == null) {
            return null;
        }

        Node parent = node.getFirstParentOfType(ASTClassOrInterfaceBodyDeclaration.class);
        if (parent != null) {
            List<ASTVariableDeclaratorId> list = parent.findDescendantsOfType(ASTVariableDeclaratorId.class);

            for (ASTVariableDeclaratorId declarator : list) {
                if (varName.equals(declarator.getImage())) {
                    ASTType nodeType = null;
                    if (declarator.jjtGetParent() instanceof ASTFormalParameter) {
                        nodeType = ((ASTFormalParameter)(declarator.jjtGetParent())).getTypeNode();
                    } else if (declarator.jjtGetParent() instanceof ASTLocalVariableDeclaration) {
                        nodeType = ((ASTLocalVariableDeclaration)(declarator.jjtGetParent())).getTypeNode();
                    } else if (declarator.jjtGetParent() instanceof ASTVariableDeclarator) {
                        nodeType = ((ASTLocalVariableDeclaration)(declarator.jjtGetParent().jjtGetParent())).getTypeNode();
                    } else {
                        System.err.println("Unknown type");
                    }

                    if (nodeType != null && MAP_CLASSES.keySet().contains(nodeType.getTypeImage())) {
                        return declarator;
                    }
                }
            }
        }

        return null;
    }
}


Here is the ruleset file:

<?xml version="1.0"?>
        <ruleset name="My custom rules"
            xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd"
            xsi:noNamespaceSchemaLocation="http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
            <rule name="ScanBigDecimal"
                  message="Avoid using BigDecimal.toString or implicit casting to string"
                  class="ScanBigDecimal">
              <description>
              Avoid using BigDecimal.toString or implicit casting to string
              </description>
                <priority>3</priority>

              <example>
        <![CDATA[
            }
        ]]>
              </example>
            </rule>
        </ruleset>


Command to test the rule:

java -cp "C:/Users/clarence/Documents/MyApps/pmd-bin-5.0.0/lib/*";. net.sourceforge.pmd.PMD C:/Users/clarence/Documents/MyTest/pmd/MyTestCase.java text bigdecimal.xml


Below is a sample test case. As said, there are cases that cannot be detected by my PMD rule. For example, concatenating a string with a BigDecimal return value from method call. Or, passing a BigDecimal to a method that takes an Object parameter and internally invoking the toString method.

Anyway, feel free to take the code and make it better~!

import java.math.BigDecimal;

public class MyTestCase {

    public static void main(String args) {
        BigDecimal bd = new BigDecimal("12.345");

        // will trigger error
        System.out.println("This is the number: " + bd);

        // this type can't be detected...
        System.out.println(bd);

        // will trigger error;
        String s = "" + bd;
        String s2 = "" + bd.toString();
        String s3 = bd + "";
        String s4 = bd + MyTestCase.returnStr();

        // can't be detected
        String s5 = "" + returnBigDecimal("12.34");
        System.err.println(returnString(bd));

        // this wont trigger warning
        String s6 = "" + bd.toPlainString();

        String s7 = "" + "a" + "b";

    }

    private static String returnStr() {
        return "hello";
    }

    private static String returnString(BigDecimal bd) {
        return "" + bd;
    }

    private static BigDecimal returnBigDecimal(String num) {
        return new BigDecimal(num);
    }
}

Monday, August 27, 2012

TP-Link TL-MR3020 as AP and router with OpenWrt

Just got a travel router TL-MR3020 and immediately flashed it with OpenWrt. By default, it will be act as an AP. Here is how to add configuration to switch between AP and router modes.

Before we start, make sure the fail safe mode is working: with the device powered off, change the switch to AP mode. Press and hold the WPS button. Turn the power on. Wait for 2 seconds until the WPS light starts to flash slowly. Immediately switch the mode to 3G while still holding the WPS button.

Now the device has IP of 192.168.1.1 and you can connect to it via a LAN cable with your computer configured with IP 192.168.1.x


With fail safe mode working, reboot the device normally. Go to /etc/config and make the following changes.

Define a new wifi network. Make copies of the file network and name them network.ap and network.router. Edit network.router and remove the lan section and add the following:
config interface 'wan'
        option ifname 'eth0'
        option proto 'dhcp'

config interface 'wifi'
        option proto 'static'
        option ipaddr '192.168.2.1'
        option netmask '255.255.255.0'


Change the wireless settings. Make copies of the file wireless and name them wireless.ap and wireless.router. Edit wireless.router and change the wifi-iface section to suit your need. Here I use WPA2
config wifi-iface
        option device   radio0
        option network  wifi
        option mode     ap
        option ssid     put_your_wireless_ssid_here
        #option encryption none
        option encryption psk2
        option key put_your_wireless_password_here


In router mode, the device withh assign IP to others via wifi. So we need to create a new DHCP pool. Make copies of the file dhcp and name them as dhcp.ap and dhcp.router. Add the following section to dhcp.router:
config dhcp wifi
        option interface        wifi
        option start            100
        option limit            150
        option leasetime        12h


Finally, we will modify the firewall rules to allow traffic from wifi to WAN. Make copies of the file firewall and name them as firewall.ap and firewall.router. Edit firewall.router and add the following sections:
config zone
        option name             wifi
        option input            ACCEPT
        option output           ACCEPT
        option forward          REJECT

config forwarding
        option src              wifi
        option dest             wan


Now we are all set. To switch between AP and router modes, just copy the above modified files to the original file and restart wifi, firewall and dhcp services:
ifup wifi
wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart


However, we can do better than that. We can map the mode switch on TL-MR3020 to auto execute the commands for us.

Create a file name /sbin/sw_ap_mode
#!/bin/sh
/bin/cp -f /etc/config/dhcp.ap /etc/config/dhcp
/bin/cp -f /etc/config/firewall.ap /etc/config/firewall
/bin/cp -f /etc/config/network.ap /etc/config/network
/bin/cp -f /etc/config/wireless.ap /etc/config/wireless
/bin/sync
/sbin/ifup wifi
/sbin/wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart


Create a file name /sbin/sw_router_mode
#!/bin/sh
/bin/cp -f /etc/config/dhcp.router /etc/config/dhcp
/bin/cp -f /etc/config/firewall.router /etc/config/firewall
/bin/cp -f /etc/config/network.router /etc/config/network
/bin/cp -f /etc/config/wireless.router /etc/config/wireless
/bin/sync
/sbin/ifup wan
/sbin/ifup wifi
/sbin/wifi
/etc/init.d/firewall restart
/etc/init.d/dnsmasq restart

Following the instructions on http://wiki.openwrt.org/doc/howto/hardware.button to edit /etc/hotplug.d/button. Then create /etc/hotplug.d/button/00-button. Execute the following commands:
uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=released
uci set system.@button[-1].handler="/sbin/sw_router_mode"
uci commit system

uci add system button
uci set system.@button[-1].button=BTN_0
uci set system.@button[-1].action=pressed
uci set system.@button[-1].handler="/sbin/sw_ap_mode"
uci commit system

Sunday, July 15, 2012

Using USB storage on Raspberry Pi

Assuming the USB storage is on /dev/sda

To mount a USB stick / disk and make it writable for the default login pi

/usr/bin/sudo /bin/mount -t vfat -o uid=pi,gid=pi /dev/sda /mnt/usb/


To create a 512M file on the USB storage and use it as swap:

# create an empty swapfile
/bin/dd if=/dev/zero of=/mnt/usb/swapfile bs=1M count=512
# create a loop device
/usr/bin/sudo /sbin/losetup /dev/loop0 /mnt/usb/swapfile
# setup swap area
/usr/bin/sudo /sbin/mkswap /dev/loop0
# enable the swap
/usr/bin/sudo /sbin/swapon /dev/loop0


To remove the swap:

# disable swap
/usr/bin/sudo /sbin/swapoff /dev/loop0
# delete loop
/usr/bin/sudo /sbin/losetup -d /dev/loop0


Umount the USB storage:

/usr/bin/sudo /bin/umount /mnt/usb/

Sunday, June 3, 2012

USB wifi for Raspberry Pi

Here are steps for setting up the USB wifi adapter TP-LINK TL-WN722N on my Raspberry Pi:


  • Edit /etc/apt/sources.list and add deb http://ftp.us.debian.org/debian squeeze non-free at the end

  • sudo aptitude update

  • sudo aptitude install firmware-atheros

  • Download the firmware
    cd /lib/firmware
    sudo wget http://linuxwireless.org/download/htc_fw/1.3/htc_9271.fw

  • Protect the file /etc/network/interfaces
    sudo chmod 0600 /etc/network/interfaces

  • Edit /etc/network/interfaces
    sudo vi /etc/network/interfaces

  • Add the following lines. Note that I am using WEP. If you are using WPA, use the fields wpa-ssid and wpa-psk instead
    auto wlan0
    allow-hotplug wlan0
    iface wlan0 inet dhcp
            wireless-essid your-ssid
            wireless-key your-hex-key

  • Bring up the interface
    sudo ifup wlan0


About the performance. Using sftp to copy /dev/zero via wifi maxed out the CPU with transfer rate at around 1.6MB/s... maybe need to test the speed without encryption...






Reference: http://elinux.org/RPi_VerifiedPeripherals#Working_USB_Wifi_Adapters