Introducing JFormica an ANT+ library for java

I have rewritten python-ant by Martín Raúl Villalb in Java. There are presently two drivers: one is a wrapper around Dynastreams own ant library for Android and the other is a javax.usb driver for ANT+ usb sticks. This allows code to be be prototyped on a development machine before adding the android specific stuff.

Features:

  • Most ANT messages have been mapped to a class, and if configurable, contain methods to adjust those configuration options.
  • Easily add listeners to individual channels with message-type filtering
  • Utility methods to wait for a response (with timeouts) to any message sent to the ant chip
  • Burst message helper function (send bursts by simply passing a byte []) – will throw an exception if transmission fails

Grab it from : https://github.com/cowboy-coders/JFormica

Here is a code sample:

class Listener implements  BroadcastListener<BroadcastDataMessage>{

@Override
public void receiveMessage(BroadcastDataMessage message) {
System.out.println("Heart rate: " + message.getData()[7]);
}

}

public void test_hrm() throws InterruptedException, TimeoutException {

// first usb ant-stick
AntTransceiver antchip = new AntTransceiver(0);

Node n = new Node(antchip);

NetworkKey key = new NetworkKey(0xB9,0xA5,0x21,0xFB,0xBD,0x72,0xC3,0x45);
key.setName("N:ANT+");

n.start();
n.reset();

// sets network key of network zero
n.setNetworkKey(0, key);

Channel c;
c = n.getFreeChannel();

c.setName("C:HRM");

ChannelType channelType = new SlaveChannelType();

c.assign("N:ANT+", channelType);

c.registerRxListener(new Listener(), BroadcastDataMessage.class);

c.setId(0, 120, 0, false);

c.setFrequency(57);

c.setPeriod(8070);

c.setSearchTimeout(255);

c.open();

Thread.sleep(10000);

c.close();
c.unassign();

//return the channel to the pool of available channels
n.freeChannel(c);

n.stop();

}

 

58 thoughts on “Introducing JFormica an ANT+ library for java”

  1. I have some experience working with ANT+ having built a arduino-based HRM although I am new to Java. I’m trying to write a simple java application to run on my laptop to capture ANT+ data via a Garmin USB ANT dongle. It looks like you’ve done all the heavy lifting in building the library… do you happen to have any example code to interface with a USB dongle? (The code at github seem to my uneducated eye to be android implementation specific.) Thanks!

  2. Firstly, sorry about the lack of documentation. The non-android code is in the jformica_jsr80 directory. You need to obtain/compile an implementation of JSR80 (java usb specification) for your operating system. I am using https://github.com/trygvis/javax-usb-libusb1 on gnu/linux, but there are implemenations around for other OSs. All the common functionality is in jformica_core and the android specific stuff in jformica_android.

    I could throw together a working example (in the form of an eclipse project) if you are interested (essentially the code above). What OS are you developing on?

    The android example turned into one hell of mess (new to android!), so please ignore that! Essentially, to adapt your code for android you just to swap out AntTransceiver for AndroidAntTransceiver, but all the locking in the example makes things very unclear. I changed the approach some what in the Cyclismo implementation.

  3. First off… thanks a lot for the response! I am also simultaneously learning java and android. I was afraid the android code sample was a bit too much to chew on given my lack of experience with it – hence why I was looking for a less platform specific code sample. I’m really just looking for a bare-bones console app that does something simple like print the heart rate to console in a never-ending loop. If you happened to have a simple example like that it would be much appreciated… (I’m developing in Eclipse for Win7 for now, but eventually would like to migrate to android apps… but I like the idea of being able to test out algorithms on a laptop first…) Again, thanks for all the work you’ve done and taking the time to respond!

  4. Like…a….charm!!!!! A bit of fiddling around with Zadig before I realized you need to tell it to look for all devices to get it to populate the drop down but then I was in the clear. Reading my HRM perfectly. Thanks for doing this! What is the best way to send you a private message?

  5. Hi, Will – very very good job!!!
    I need your help, if it is possible.
    I’m try to write an ANT+ application in PROCESSING (http://www.processing.org/).
    I’m searching for an ANT+ Library that I can rapidly test and use in that environment.
    I need to read 3 or 4 ANT+ heart rate chest and 1 ANT+ weight scale (for weight and BMI).
    Could you help me please?
    Thanks

  6. Hi

    I got a speed/cadence example working today. I couldn’t get my Garmin 2 USB stick to work under Windows. The usb4java layer sees something is plugged in but can’t access it.

    It worked out of the box under Linux but I often get a Timeout and have to start the program multiple times. I’d like to exchange information with anyone else using this library.

    Great work.

  7. @David George

    Are these timeouts occuring when the channel is being set up or in general operation? I suspect it could be bug in thr JSR80 AntTransceiver class:

    https://github.com/cowboy-coders/JFormica/blob/master/jformica_jsr80/src/main/java/org/cowboycoders/ant/interfaces/AntTransceiver.java

    If you could enable Level.ALL logging on that class:

        public static final Level LOG_LEVEL = Level.ALL;

    public static void setupLogging() {
    // set logging level
    AntTransceiver.LOGGER.setLevel(LOG_LEVEL);
    ConsoleHandler handler = new ConsoleHandler();
    // PUBLISH this level
    handler.setLevel(LOG_LEVEL);
    AntTransceiver.LOGGER.addHandler(handler);
    }

    , and from that we should be able to tell where it is hanging. I know I had to set the timeout to zero in javax.usb.properties when changing from javax-usb-libusb1 to usb4java:

    de.ailis.usb4java.timeout = 0

    I rely on:

     inPipe.syncSubmit(data);

    not timing out in AntTransceiver.UsbReader. There may be a better implementation.

    Regarding Windows, Is an Exception being thrown? Please post the details.

  8. Hi,

    That’s very interesting. Yes it is generally when things are set up. The problem seems to be if the unit, in this case Speed+Cadence, hasn’t started transmitting we get the exception. If I set the timeout to 2000ms things work fine but the transmission rate appears slower.

    I will do what you have said with debug above and report back.

  9. Yes the error comes from the inPipe.syncSubmit. It seems we timeout in the usb4java code if data isn’t ready within a certain time:

    de.ailis.usb4java.libusb.LibUsbException: USB error 7: Transfer error on bulk endpoint: LIBUSB_ERROR_TIMEOUT

    Now with the HRM you are probably already transmitting it when you start the program so don’t see this error as a regular stream of data is coming through. This may not be the same for Speed/Cadence.

    I trapped the error higher up in the code then continue thus:-
    try {
    // inPipe.open();
    LOGGER.finest(“pre read”);
    inPipe.syncSubmit(data);
    } catch (UsbException e) {
    continue;
    } finally {
    // inPipe.close();
    }

    What do you think?

    I will have a look at the windows problems but it seems my whole usb4java setup is wrong somewhere.

  10. @David George

    Thanks for looking into this. When reading the javax.usb examples. I was under the impression USB bulk transfers on an input pipe should not timeout:

    294     try {
    295     /* This is synchronous, meaning our Thread will
    296     * block until the data buffer has been filled by the device.
    297     * For non-blocking submission, the asynchronous method should be used.
    298     * Note that interrupt-in (and bulk-in) pipe submissions may
    299     * block indefinitely! Control-type and isochronous-type submissions
    300     * will complete (or fail) in a finite amount of time; the USB spec
    301     * arbitrarily sets this time limit at 5 seconds, but YMMV with
    302     * various implementations. Interrupt-out (and bulk-out) shouldn't
    303     * block indefinitely.
    304     */
    305     length = usbPipe.syncSubmit(buffer);

    source : http://javax-usb.cvs.sourceforge.net/viewvc/javax-usb/javax-usb-example/src/MouseDriver.java?revision=1.1&view=markup

    Not being a usb expert, I would be slightly concerned at what would happen if the timeout occured mid transfer – would data be lost as we don’t retry? That is why I set de.ailis.usb4java.timeout to zero (no timeout). Admittedly this is far from ideal for transfers on the outpipe, but I’m yet to experience a problem. I don’t think setting de.ailis.usb4java.timeout to zero would affect message throughput as it will only block whilst waiting for a message.

    Edit: I have asked the developer of usb4java if his code is working as designed: https://github.com/kayahr/usb4java/issues/16#

  11. If I set de.ailis.usb4java.timeout = 0 the code will often just hang.

    I made some mods to AntTransceiver to use the receive length to process messages. This saves unnecessary looking for sync bytes. Do you want these changes.

    I was also concerned about the slow rate of data – about one message every two seconds. I see my stick sends a lot of:

    A4 03 40 00 01 02 E4 – Received channel event, ’02’ => EVENT_RX_FAIL

    which looks like the Ant key was expecting some message which it never got.

    Do you see anything similar?

  12. Fixed the EVENT_RX_FAIL issue, I had the Speed period value in my set-up code not speed and cadence (8086). Doh!.

  13. Yep, the normal reasons for EVENT_RX_FAIL events are:

    – mismatched channel period
    – poor reception (poorly designed antenna, beyond maximum range, etc)

    Regarding the syncs, I wasn’t sure that we wouldn’t receive two messages in the same buffer or have messages spanning two buffers, hence the scanning of the excess bytes beyond the message length. Admittedly I have never seen this is in reality, so like you say it may be slightly more efficent to assume the sync byte is always at index zero, but I don’t think the current method is too taxing. Are we sure that each read will only contain one message? I thought the syncs might be there for a reason,but maybe they are just a relic from the wireless transmission (preamble -> sync -> message).

  14. Hi,

    I’ve got everything working on Linux and Windows. I can send you my updated files: one for the speed and cadence sensor and the other for AntTransceiver where you’ll see the changes I made. If you want I can check them into Git directly.

    For Windows I had to use libusbk from their website, not the one that installs with Zadig:

    http://libusbk.sourceforge.net/UsbK3/index.html

    Great work, thanks for your library.

  15. Nice work, thanks for getting this working. I was going to implement the cadence/speed sensor for cyclismo at some point, so your code will be really useful! To get these changes merged in could you either: create a fork that I can pull from, or email me the files.

    Interesting that you had to install libusbk manually – from my very limited testing, Zadig worked for me. I will update the README to reflect your experiences.

    Thanks again, Will.

  16. Hello, i wanted to make a desktop application to communicate with a scale tanita bc1000 via ant+ can you tell me if you think its possible.

    thanks

  17. @Leandro Oliveira

    Should be possible. Check out the demo source in http://www.thisisant.com/resources/legacy-android-ant-sdk/ ((specifically AntPlusManager.java) for device configuration settings and decoding info.

    Update:

    Here are some initial settings : http://wiki.cowboycoders.org/wiki/Tanita_scales. I believe the scales may send some magic packets, so you may need to snoop usb traffic to determine these. Alternatively if you give me the dump, I can check it out.

  18. Hi Will

    I’ve now got an ANT HRM. I’ve tested it with your basic heart rate monitor code.

    Do you have an idea how to combine the HeartRate Monitor with the Speed and Cadence sensor. I assumed I would just have to ask for a second channel and configure with the correct parameters for my HRM. However I get a Timeout waiting for reply on my second channel in org.cowboycoders.ant.Channel.assign

  19. To answer my own question this because the Channel was not being marked as in use in the JFormica code I had. This is fixed in the latest version. I will supply a combined HR/Speed and Cadence example later.

    1. Sorry for not replying – didn’t get an email for some reason! Let me know if you have any problems implementing the combined example and thanks for your contributions.

  20. I tried using the example code for ANT USB-m but I keep getting an error “Device Not Found” at the start of the code AntTransceiver antchip = new AntTransceiver(0);

  21. I tried using the example code for ANT USB-m but I keep getting an error “Device Not Found” AntTransceiver antchip = new AntTransceiver(0);
    I use a Garmin ANT + key works with other software (power rotor).

  22. I tried jformica_java release.

    I am looking for a version of “pure java” (no android) to use it with a Java EE Web application.
    I watched your version (feature_improve_netkeys branch) but it seems only compatible with Android.

    Do you have a Java SE compatible with most ANT + key?

    1. ANT USB-m support is only in that branch. It will be Java SE compatible if you don’t use the code from the jformica_android subproject.

  23. Has anyone got a working example of listening to multiple similar devices (eg. >1 HRM or Power Meter)? How do you differentiate between the data streams on specific device profile channel?

  24. I need your help, if it is possible.
    I’m try to write an ANT+ application in PROCESSING (http://www.processing.org/).
    I’m searching for an ANT+ Library that I can rapidly test and use in that environment.
    I need to read 3 or 4 ANT+ heart rate chest and 1 ANT+ weight scale (for weight and BMI).
    Could you help me please?
    Thanks

  25. Hello
    Is it possible to open both channels at the same time to interact with a speed sensor and a pulse sensor
    on the same key + ant because if you open both channels at the same time the following error in this product:

    org.cowboycoders.ant.interfaces.AntCommunicationException: Error opening inPipe
    at org.cowboycoders.ant.interfaces.AntTransceiver.start (AntTransceiver.java: 503)
    at org.cowboycoders.ant.events.EventMachine.start (EventMachine.java: 212)
    at org.cowboycoders.ant.Node.start (Node.java: 251)
    at com.ciris.ant.speedandcadence.StarterSC.run (StarterSC.java: 45)

    Thank you for your answers

    1. You should be able to do this… Make sure you are using the code from the branch feature_improve_netkeys. If you are, please post the code you using.

    2. This will open two channels to an ANT+ Heart Rate Monitor and to a Speed and Cadence sensor. I use separate Callback objects for the two channels.

      public void open(int scID, int hrmID) {
              /* must be called before any configuration takes place */
              node.start();

              /* sends reset request : resets channels to default state */
              node.reset();

              // specs say wait 500ms after reset before sending any more host
              // commands
              try {
                  Thread.sleep(500);
              } catch (InterruptedException ex) {

              }

              // sets network key of network zero
              node.setNetworkKey(0, key);

              scChannel = node.getFreeChannel();

              // Arbitrary name : useful for identifying channel
              scChannel.setName("C:SC");

              // choose slave or master type. Constructors exist to set
              // two-way/one-way and shared/non-shared variants.
              ChannelType channelType = new SlaveChannelType();

              // use ant network key "N:ANT+"
              scChannel.assign("N:ANT+", channelType);

              // registers our Heart Rate and Speed and Cadence callbacks with the
              // channel
              scChannel.registerRxListener(SCListener, BroadcastDataMessage.class);

              // ******* start device specific configuration ******
              scChannel.setId(scID, ANT_SPORT_SandC_TYPE, HRM_TRANSMISSION_TYPE,
                      PAIRING_FLAG);

              scChannel.setFrequency(ANT_SPORT_FREQ);

              scChannel.setPeriod(ANT_SPORT_SPEED_PERIOD);

              // ******* end device specific configuration ******

              // timeout before we give up looking for device
              scChannel.setSearchTimeout(Channel.SEARCH_TIMEOUT_NEVER);

              // start listening
              scChannel.open();

              // *** Heart Rate Monitor ***

              hrChannel = node.getFreeChannel();

              // Arbitrary name : useful for identifying channel
              hrChannel.setName("C:HRM");

              ChannelType channelType2 = new SlaveChannelType(); // use ant
                                                                  // network key
                                                                  // "N:ANT+"
              hrChannel.assign("N:ANT+", channelType2);

              // registers an instance of our callback with the channel
              hrChannel.registerRxListener(HRListener, BroadcastDataMessage.class);

              // start device specific configuration

              hrChannel.setId(hrmID, HRM_DEVICE_TYPE, HRM_TRANSMISSION_TYPE,
                      PAIRING_FLAG);

              hrChannel.setFrequency(ANT_SPORT_FREQ);

              hrChannel.setPeriod(HRM_CHANNEL_PERIOD);

              // ******* end device specific configuration

              // timeout before we give up looking for device
              hrChannel.setSearchTimeout(Channel.SEARCH_TIMEOUT_NEVER);

              // start listening
              hrChannel.open();
          }
  26. Thank you for your reply. I managed to do that I just had a small error in my code. Do you know someone who would have done with your library code to capture power. I have found in the doc (https://devzone.nordicsemi.com/documentation/nrf51/4.4.1/html/group__ant__bicycle__power__minimum__receiver.html) some given that my driver’s receive data with a power tap wheel but the data received with the method getUnsignedData () gives me no usable data

    cordially
    Roz christophe

  27. Will,

    Are there any plans to implement ANT-FS in JFormica? I’m looking for a Java ANT-FS solution, and was wondering if you were planning on heading in that direction.

    Conrad

  28. Hello,

    I’m trying to make an android application with your library but when i try your sample application (HRM), i get this exception: org.cowboycoders.ant.messages.responses.exceptions.InvalidMessageException

    It occurs at this line: node.setNetworkKey(0, key);

    The stacktrace:

    com.example.jformica_hrmexample E/Formica Hrm Ex﹕ Caught Exception: org.cowboycoders.ant.messages.responses.exceptions.InvalidMessageException
    org.cowboycoders.ant.messages.responses.exceptions.InvalidMessageException
    at org.cowboycoders.ant.messages.responses.ResponseExceptionFactory.map(ResponseExceptionFactory.java:61)
    at org.cowboycoders.ant.messages.responses.ResponseExceptionFactory.throwOnError(ResponseExceptionFactory.java:81)
    at org.cowboycoders.ant.Node$TransmissionErrorCondition.test(Node.java:117)
    at org.cowboycoders.ant.Node$3.test(Node.java:572)
    at org.cowboycoders.ant.events.EventMachine$IncomingMessageListener.getReply(EventMachine.java:138)
    at org.cowboycoders.ant.events.EventMachine.waitForCondition(EventMachine.java:196)
    at org.cowboycoders.ant.Node$4.execute(Node.java:585)
    at org.cowboycoders.ant.Node$ThreadedWait.call(Node.java:80)
    at org.cowboycoders.ant.Node$ThreadedWait.call(Node.java:1)
    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
    at java.lang.Thread.run(Thread.java:856)

    I have this error with master and feature_improve_netkeys branch.

    Have you an idea for this error ?

    Thanks you for your answer.

    Best regards

    Mickael

    1. An InvalidMessageException is usually thrown when the message is malformatted, but it may be the case that in the new android API they have disabled the ability to set the network key.

  29. Hi Will, nice work there. It is odd, however, that the software written in C# supplied by ThisIsAnt works fine with libusb and Java needs libusbK to function. Do you know any details on why is that?

  30. Hi Will,
    i’ve got a question while I’m using your great work with android. Using it with linux is fine.

    When I’m trying to setup the interface in an android app I always get an exception during node.start().
    It’s an AntCommunicationException which says “timeout for initialconfiguration to complete”. It happens within AndroidAntTransceiver.startHelper() during waitForServiceConnected();

    It doesn’t matter if I’m using your example or my app.

    Any suggestions?
    Thanks
    Uwe

  31. Hey everyone,

    I am trying to get an android app on a raspberry pi running android OS. I can only install android 2.3.3 (API 10) on the raspberry pi. Will these library’s work if I change the min SDK in the android manifest file??

    Thanks

  32. Hello, I’m intersted to ant+ protocol and your library.
    Do you think is possible to discover continually all ant+ devices passing in an area?
    If all cyclists have a ant+ device (hrm or speed sensor) is it possible to write an application that detects passing all cyclists on the finish line, using for example an ant+ usb stick?

  33. Hello! I’m trying to read HR from Mio Fuse. I tried your example https://github.com/cowboy-coders/jformica_examples. I did all steps from ReadMe file. Unfortunately, I get this exception: java.util.concurrent.TimeoutException: timeout waiting for message
    at org.cowboycoders.ant.Node.sendAndWaitWithAdapter(Node.java:537)
    at org.cowboycoders.ant.Node.sendAndWaitForMessage(Node.java:676)
    at org.cowboycoders.ant.Node.sendAndWaitForMessage(Node.java:598)
    at org.cowboycoders.ant.Node.getCapabilityResponse(Node.java:283)
    at org.cowboycoders.ant.Node.init(Node.java:307)
    at org.cowboycoders.ant.Node.start(Node.java:255)
    at org.cowboycoders.ant.examples.demos.MultiHeartRateMonitor.main(MultiHeartRateMonitor.java:278)
    июл 01, 2016 1:52:44 PM org.cowboycoders.ant.Node getCapabilityResponse
    WARNING: getCapabilityResponse : timeout

    What could be wrong? Thanks for answer!

    1. It looks like your ant usb stick didn’t respond to a request for it’s capabilities (which is used to determine the number of ant channels iirc). Which device are you using?

  34. Hi, I am trying to write a java code in eclipse using the jformica_examples. I have a tanita scale BC-1100F and i want to get all the body composition measures. This scale have open protocol for all measurements. The application can receive three message, the page 1, 80, and 81. In the page 1 is supposed comes the body weight, but always shows 0xFFFE = Computing/Idle. Do you know what i have to do to pair the scale?. Is supposed that a led in the scale should light to make the measure, but I have not succeeded. Can you tell me how to send a page request to the scale? Do you have a document with a description of all functions?

    Thank you very much

  35. Hello, I wonder if this library I can send data via ANT+, specifically the power generated. If so, they could give me an example?

    Thanks in advance

  36. Just trying to setup on my PC. Have one ant stick plugged in which I know is working. Tried running a couple of demos but they fail on “new AntTransceiver(0);” part of line with

    Exception in thread “main” java.lang.NullPointerException
    at javax.usb.UsbHostManager.getServicesName(UsbHostManager.java:96)
    at javax.usb.UsbHostManager.initialize(UsbHostManager.java:32)
    at javax.usb.UsbHostManager.getUsbServices(UsbHostManager.java:24)
    at org.cowboycoders.ant.interfaces.AntTransceiver.doInit(AntTransceiver.java:141)
    at org.cowboycoders.ant.interfaces.AntTransceiver.(AntTransceiver.java:125)
    at org.cowboycoders.ant.interfaces.AntTransceiver.(AntTransceiver.java:114)
    at virtualride.AcknowledgedDemo.main(AcknowledgedDemo.java:196)

    Any Ideas? Thanks

    1. What operating system are you using? Does the
      the vendorId, deviceId pair of your usb stick correspond to 0fcf and 1008 respectively? Would be grateful if you could report this on github, thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *