Part 2 - Internet connected user-space device drivers
In this part 2 of 2-part series, I am going to show how to use Balena to make our USB detector an IoT device. Read part 1 here
Table of contents
- Basic Dockerization and Balena
- Passing C data structures to Golang
- Epilogue
Dockerization and Balena
I am following the steps for a BeagleBone Black from here, but you could follow any as long as you pick Go as the language of choice
- After
git clone https://github.com/balena-io-projects/balena-go-hello-world
, you can check out the basic starter app that the nice folks at Balena have written. It looks something like this -
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello, world (from go!)\n"))
})
addr := ":80"
fmt.Println("Example app listening on port ", addr)
log.Fatal(http.ListenAndServe(addr, nil))
- A simple http server that replies with a hello world no matter what you say.
- Balena allows you to deploy using
git
which is kinda neat, because that means that you don't have to cross compile on your host machine - We are going to need the
libusb-dev
package on our BeagleBone Black. I simply created a script calledinstall.sh
and added it to my Docker file like below –
FROM balenalib/%%BALENA_MACHINE_NAME%%-golang:latest-build AS build
WORKDIR /go/src/github.com/balena-io-projects/app
COPY /app ./
RUN ./install.sh
RUN go build
FROM balenalib/%%BALENA_MACHINE_NAME%%-debian:stretch
COPY --from=build /go/src/github.com/balena-io-projects/app/ .
CMD ./app
- If you want to look at the
Dockerfile
before I added my changes, you can see it here - My
install.sh
file looks like the following –
- Don't forget to make the
install.sh
executable usingchmod +x install.sh
- Another thing that I realised is that it's better to create an image with the dependencies installed rather than install it after you install the base image, but I am going to skip that right now under the guise of premature optimization.
- Once you have deployed it, you should be able to see the logs on the console and a simple Hello World on port 80 of your local IP address. Balena gives an optional Public addres flag, that if you enable, you can access this over good ol Internet.
Passing C data structures to Golang
Structure padding means adding bytes between two types in a struct to make memory allignment possible. You can read more about the limitations of golang on the CGo wiki. More reading material that I found
- Liam Kelly's blog post here
- Dave Cheney's blog post here – aptly titled "Padding is hard" :)
- This stackoverflow answer
cJSON
I think a simpler way is to use something like protobuf
or json
. It's a bit more code, but it'll be a lot better than what I got working with our USB C code by adding attribute((aligned(8)))
in the structure declaration.
On the Go side, we have a standard library for handling json
, so that should be less tedious.
"Epilogue"
I have some more thoughts about building embedded systems using Go and C, namely -
- Goroutines and multicore archs for hardware I/O
- OTA updates and logging for network constained devices
- AWS IoT C SDK + Golang (AWS Go SDK doesn't support MQTT)
- Managing state machines
I'll be talking more about the above on this blog as I dive deeper.
Cheers!