Hello, world.
I’m always working on side-projects with some sort of gadget, but I don’t really have a record of any of them, so I figured I’d start a blog. This is just for me, for now, but if you’re reading this, hello!
My wife and I bought our condo several years ago, and I’ve always been curious to map out the temperature and humidity of various regions of the building, specifically the basement. I’m going to document my work here, mostly to go through the exercise of documenting my work.
I’m going to set up a system of sensors around the house that report back to a gateway that uploads the data to a centralized server in some way.
At work awhile ago we were evaluating a few different SOMs (System on a Module) for our products. I bought some evaluation kits for personal use as well, since they seemed pretty cool. I bought both a Wandboard Quad and an Artik530 Starter Kit kit.
These are both fanless boards that have on-board Wi-Fi and Bluetooth radios. They are also both very easy to compile relatively recent Linux kernels for (4.0+), which is great for playing around with. For no particular reason, I found myself working with the Artik board a bit more.
I’ve always loved working with various sensors (show me an engineer who doesn’t love sensors), and some that I like a lot are these Si7021 Humidity and Temperature sensors by Silicon Labs. They’re accurate, easy-to-use and have an available datasheet. We used these at a company that I started years ago, and they worked quite well.
I bought a couple of cheap Bluetooth Low Energy (BLE) radios off of Amazon (DSD Tech SH-HC-08.
These aren’t super fancy, but seemed pretty easy to use. The documentation
for these parts is a little suspect; when I first received these radios, I tried talking to them and found
that the radio doesn’t respond to all of the commands listed in its datasheet. The AT
/OK
command/response works
fine, but I haven’t been able to get AT+ADDR?
to work.
I have some Adafruit Trinkets laying about that I’ll use to perform the sensor readings and to control the Bluetooth radios. They’re tiny and fairly cheap, but at the expense of limited pins (e.g. no UART interface, so for a serial port we’re going to have to bit-bang). These might end up needing to be replaced by something else, but I have them, so I might as well give them a shot.
I use Yocto quite a bit at work. Yocto is a tool (really: collection of tools) for generating custom Linux distributions.
I know it reasonably well and used it to quickly get up and running on my Artik530 board. The reference distribution of
Yocto is called poky
. This is versioned according to alphabetical names; at the time of this writing, sumo
is the most
recent development branch.
Unfortunately, Samsung doesn’t maintain a Yocto layer for developing for their boards, but they do provide an SDK for compiling the kernel, u-boot and a couple of distros, so porting it to Yocto isn’t too bad. I scrounged around the internet and found some old layers that I was able to lean on, and pretty quickly was able to get an SD card image building.
I’ve been wanting to play with a different language, so for no real reason I’ve chosen to use Go as the programming language to develop this project with. I’ve been hearing about it a bit at work, and one of my coworkers loves it. It also helps that he’s one of the most talented people I’ve had the privilege of working with (Michael Fogleman, blog).
Go is actually a nice choice because it’s very easy to cross-compile, and generates statically-linked binaries, so it doesn’t require a lot of toolchain structure to work with. The downside is that I don’t really know how to make it work with Yocto.
I plan to use some of the standard Linux utilities for managing Bluetooth connections, so that I don’t have to reinvent the
wheel. I’m using BlueZ 5.48, mostly because that’s the version that is standard in Poky version sumo
. BlueZ provides the
bluetoothd
daemon, which manages the Bluetooth hardware in the system, and also exposes a D-Bus interface for communicating
with other system processes.
Fortunately, there is a Go library for communicating using D-Bus: dbus. There is even a nice
library for communicating via D-Bus with BlueZ: go-bluetooth. go-bluetooth
definitely
seems like it could use some help; if I end up with useful Go, I should push that back upstream. One thing that jumps out to me
is that there are calls to exec.Command, which I don’t love. I’d prefer this tool to be
entirely dependent on the D-Bus communication. Perhaps these calls are there for reasons that aren’t immediately obvious to me.
Well, the first thing I want to do with Go is to write a small program that can list the Bluetooth adapters that are available.
I’ve used the BlueZ tools to ensure that my system is set up correctly (I can turn the Bluetooth radio on and off, I can discover
devices in the area). I note that I’ve included rfkill
in my Linux distro – I should probably remove that because it’s not
adding any value to me.
Reading through the BlueZ 5 API introduction and porting guide,
I notice that things have changed a bit since I last used the BlueZ D-Bus interface: I used to be able to make a call to
ListAdapters()
and receive a list of Adapters that BlueZ was managing. It appears that this has been replaced by a much more
generic interface ObjectManager.GetManagedObjects
.
In reading through the go-bluetooth
code, it appears that we want to instantiate an instance of the Manager
class:
manager, err := api.NewManager()
So at this point, the manager
object will be receiving events and such, but our program still doesn’t have a list of Bluetooth
adapters. To get that, we have to set up a callback for the events that the manager
object is receiving:
// Note: err was defined above, so I can re-assign its value here.
err = api.On("adapter", emitter.NewCallback(func(ev emitter.Event) {
adapterEvent := ev.GetData().(api.AdapterEvent)
log.Printf("Found adapter: %s, Path: %s, Status: %d", adapterEvent.Name, adapterEvent.Path, adapterEvent.Status)
}))
This sets up an anonymous function as the callback for adapter
events – this code will seemingly cast the argument from an
Event
to an AdapterEvent
, which we can use to figure out what the event was.
Ok, I am tired and will be signing off for today. Here is the final code that I am running:
package main
import (
"github.com/godbus/dbus"
"github.com/muka/go-bluetooth/api"
"github.com/muka/go-bluetooth/bluez/profile"
"github.com/muka/go-bluetooth/emitter"
"log"
)
func main() {
manager, err := api.NewManager()
if err != nil {
log.Fatalf("Failed to get new Manager: %s", err)
}
defer api.Exit()
err = api.On("adapter", emitter.NewCallback(func(ev emitter.Event) {
adapterEvent := ev.GetData().(api.AdapterEvent)
log.Printf("Adapter: %s, Path: %s, Status: %d", adapterEvent.Name, adapterEvent.Path, adapterEvent.Status)
if adapterEvent.Status == api.DeviceAdded {
log.Printf("Adapter Added!")
}
}))
err = manager.RefreshState()
if err != nil {
log.Fatalf("Failed to refresh Manager state: %s", err)
}
select {}
}
GOOS=linux GOARCH=arm GOARM=7 go build bluetooth.go
Next, I will set up this code to scan for devices. I also will need to make sure that my code can enable/disable the BlueZ adapter, in case the program starts while the interface is disabled.
After that, I’ll have to look for devices that expose a specific specific GATT profile, which I will be using for transferring data from my sensors to the gateway.