UART bridge with HM-10: communication between microcontroller and Java application on your PC

Today we're going to explore the exciting world of (relatively) new wireless technologies with Bluetooth Low Energy modules.

BLE was developed with power conservation in mind, a standard made to fit the market of battery-powered devices that can go on for months and even years without being recharged. What I had in mind for a module I purchased online was replacing Bluetooth 2 UART wireless modules with something modern.
The module we're going to use is called HM-10. It was conceived by Jinan Huamao Technology company and as usual it can be found online here -> original, and as with all novel promising pieces of hardware there are appreciably cheaper clones on the market, too. They're supposed to perform the same as original, though I haven't had a chance to test one of those.

Their line of products also includes HM-11 which is basically the same module with smaller footprint and fewer pins broken out. You can use HM-11 instead of HM-10 freely within this tutorial (here are links to original module and cheaper fake).

Hardware setup

HM-10 pinout diagram

HM-10 can accept 2 - 3.7V as power input, so in order to quickly whip up a testing rig without all that voltage level converting stuff we'll need a 3.3V Arduino to act as a host MCU to BLE module.

Pro Micro - 3.3V/8MHz is the most widespread 3.3V Arduino. Again, you can find it cheaper online here.
Pro Micro - 3.3V/8MHz

In case you don't want to wait for a proper dev board to arrive by post and you have an Arduino Uno handy, you can retrofit it to be 3.3V-powered (see my blog post about various Arduino improvements).

3.3V-converted Uno with HM-10
Connections are as follows:

Arduino pin HM-10 pin
Vcc Vcc

Now let's just hook it all up according to table above and upload test sketch that passes everything as it is between SoftwareSerial port on pins 2 and 3 (that's where we have HM-10 connected) and Hardware Serial which we can see in Serial monitor window.
Send AT through Serial monitor a couple of times (line termination should be set to 'No line ending'). If you see OK sent back to you, then the connections works.

You'll also need a BLE dongle to connect your PC to HM-10. I found these work well.

Tiny BLE dongle that I have.

Software setup

Prep up your environment

You'll need any Linux distribution to make use of Intel's Bluetooth LE library (GitHub link). I'll be using Arch Linux, therefore all the console commands below will work on any Arch Linux distribution and on any other Linux distro with minor modifications (e.g. different package manager, different package names).
So here's what you'll need:
  • Yaourt or any AUR-enabled package manager
  • IntelliJ IDEA Community Edition or any Java IDE of your choice (Eclipse, Netbeans, vim+javac etc...)
  • Git, CMake, Doxygen
  • Bluez - Bluetooth library for Linux
  • Arduino IDE

In CMake-GUI window, click Configure button. In freshly-appeared settings tick BUILD_JAVA and change CMAKE_INSTALL_PREFIX to /usr (if you don't you'll get java.lang.UnsatisfiedLinkError: no tinyb in java.library.path error later). Click Generate and we're done here.

CMAKE config.

Now you have TinyB library installed in your system. There's a note in README file telling that in order for it to operate correctly, you should add -E flag to bluetooth service, but it seems that Bluez v.5.46 that I'm using has already moved BLE support from experimental to basic functions. Therefore, you probably won't need to do any further modifications too.

ArchWiki has a splendid article on setting up Bluetooth in your system. It's basically just starting bluetooth.service (and enabling it if you can't be bothered to start it after each reboot). Check if it works by following Bluetoothctl section of an article and performing a trial run of scanning.

Let's get to programming then!
BLE is structured unlike Bluetooth 2 and 3. You can read detailed software-related aspects of BLE on Wiki.

In short, BLE devices expose services with unique IDs, and each service may have several characteristics. You can think of characteristics as variables that can have READ, WRITE or READ and WRITE access.

Each BLE device, HM-10 included, has a set of predefined services and characteristics that contain info about the device (e.g. device name). Beside those, it has exactly one custom service with one characteristic that we'll use to send and receive serial data. Detailed description of all characteristics and services follows below:

To transmit data from HM-10 to host device: write it to HM-10 serial port. It then sends a notification to host device to say there is new data available.

To receive data sent by HM-10: Subscribe to value change notification on custom characteristic. It works like a callback - when there is new data available, the function you supplied when you subscribed is called to handle incoming data.

To transmit data from host device to HM-10: write it to custom characteristic. It will instantly be sent from HM-10 serial port to whatever device it's connected to.

To receive data sent by host device: just stay tuned on module's RX/TX lines :)

Custom characteristic length: 20 ASCII characters, so to send a string longer than 20 characters you'll have to split it to 20-char segments and send them in sequence.

Java coding

I'll be basing my code on examples that came with tinyb library. You can find them in tinyb/examples/java. Not much of them, but it's enough to get started fast. example is supposed to receive temperature from some sort of BLE temp. sensor, let's change it and complement a bit to be able to send and receive data from HM-10.

So here's the code -> IntelliJ IDEA project. It works with tibyb library version 0.3, haven't checked any other versions yet. Just connect Arduino with HM-10 and BLE dongle to USB port,  launch the Main class and behold 'hello' string being sent each second from your Java code to HM-10 (and Arduino) over the air!

It works!

Note that for some reason this library can not discover Generic access service, but I didn't need it anyway. :-)
You can also type something back in Serial Monitor and see it received through UARTReceiveNotification's callback.

Now that the feasibility of sending text back and forth over BLE has been proven, all that's left is to refactor this crappy sample code that I quickly fudged together.

Here's the final refactored project that gives programmer transparent serial connection with HM-10 BLE module. It could be tailored to connect to a similar module by changing device's address, UART service and characteristic address and its length.

Here's how to use it (IntelliJ IDEA project covers every use case possible):
  • Initializing:
  • Writing: input is a string of any length. It will be split into 20-char fragments inside.
  • Reading.Can be callback-based: ..or polling-based:

Feel free to use it for anything you want (try to avoid killing someone though) and leave suggestions in comments below. Bye!