Hoplon & MQTT Tutorial

I’ve been wanting to play around with the Hoplon (Clojure web application framework) for while now and after prototyping an MQTT based system for work, I decided to bring them together.

The Goal

Use MQTT to push status messages to a Hoplon status page

Overview

This system has three parts:

  • MQTT Broker
  • MQTT client publisher (for publishing messages to the broker)
  • Hoplon status page – MQTT client subscriber (also a client) (for subscribing to the topic and displaying the most recent status message)

MQTT Broker

The Broker is the hub through which all message are sent and received. Sending a message to a broker is called “publishing”. When you publish a message, you publish it to a “topic”. The topic is like a channel. To receive messages, a client “subscribes” to a topic.

In my example I’m going to publish messages to the “hoplon-mqtt-status” topic. You can do some awesome things with topics. Read more about them at http://mqtt.org/wiki/doku.php/topic_format.

For the sake of simplicity, we are going to use test.mosquitto.org as our broker

MQTT client publisher

Since Hoplon is written in Clojure and I’m using the Machine Head library for the MQTT, I derived a simple client from the the Machine Head examples to publish our status to the topic “hoplon-mqtt-status”. I’ll do this in a Clojure REPL with the help of lein try.

Hoplon status page

I’ve modified the Castra Simple example to subscribe to the topic “hoplon-mqtt-status” and display the current status.

Building it

MQTT Broker

Nothing to set up here. The folks at Mosquitto.org provide a test server at test.mosquitto.org. We’ll use the open port 1883.

MQTT client publisher

  • Make sure you have lein try set up.
  • Open your terminal
  • lein try clojurewerkz/machine_head "1.0.0-beta8" - This should give you a Clojure REPL with the Machine Head dependencies
  • (require '[clojurewerkz.machine-head.client :as mh])
  • (def conn (mh/connect "tcp://test.mosquitto.org:1883" (mh/generate-id) {})) - Make a connection to the broker
  • (mh/publish conn "hoplon-mqtt-status" "On") – Publish the message “On” to the topic
  • (mh/publish conn "hoplon-mqtt-status" "Off") - Publish the message “Off” to the topic
  • See more examples of Machine Head on Github

Okay, our publishing client is ready in the REPL. On to Hoplon.

Hoplon status page

  • git clone git@github.com:tailrecursion/hoplon-demos.git - Clone the Hoplon demos repo
  • cd hoplon-demos/castra-simple/ – Move into the Castra Simple project
Edit build.boot
  • Remove line  5 – (load-file "../build.util.clj")
  • Remove line 6 – (require '[build.util :as build])

Update set-env!

(set-env!
 :repositories #{"https://repo.eclipse.org/content/repositories/paho"}
 :dependencies '[[tailrecursion/boot.task "2.2.3"]
 [tailrecursion/hoplon "5.10.14"]
 [clojurewerkz/machine_head "1.0.0-beta8"]]
 :out-path "resources/public"
 :src-paths #{"src"})
Edit src/index.cljs.hl
  • Change line  4 to: (defc= message rpc/message)
  • Change line 12 to: (h1 (text "Status: ~{message}"))))
  • NOTE: watch your parenthesis!
Edit src/app/rpc.cljs
  • Change line 8 to (defc state {:message "Waiting for status..."})
  • Change line 12 to (defc state {:random nil})
  • REALLY! Watch your parenthesis!
Edit src/app/api.clj to look like this:
(ns app.api
 (:require
 [tailrecursion.castra :refer [defrpc]]
 [clojurewerkz.machine-head.client :as mh]))

(def message (atom "Initial message..."))

(def conn (mh/connect "tcp://test.mosquitto.org:1883" (mh/generate-id)))

(defn handle-delivery
 [^String topic _ ^bytes payload]
 (swap! message (fn [& x] (String. payload "UTF-8"))))

(defrpc get-state []
 {:message @message})

(mh/subscribe conn ["hoplon-mqtt-status"] handle-delivery)

Then, in another terminal, run: boot development to start Hoplon development server

Open http://localhost:8000/ in your browser.

Sending message

Go back to the Clojure REPL and run (mh/publish conn "hoplon-mqtt-status" "On")

Change the text from “On” to anything you’d like and publish again. Watch as the browser status updates.

Notes

So… this is all a little light on explanation. My first goal was to get the tutorial out there. I’ll be adding explanations as I find more time.

Further Exercises for the Reader

  • Add controls to the Hoplon status page to publish back to the topic

If you think you can temper yourself into manliness by sitting here over your books, supposing you will grow into it as a matter of course, by a rule of necessity, in the same way as your body grows old, it is the very silliest fancy that ever tempted a young man into his ruin. You cannot dream yourself into a character; you must hammer and forge yourself one.

James Anthony Froude

Geeky trip planning using the dstk gem

Background

The following is an almost-true story that may or may not have happened earlier this summer.

Setting

I’m sitting on the couch with my wife one night when she reminds me that we need to plan our trip to Oregon.

Action

“I found this list of places to visit on Google”, she says, pulling up a website.

(the table below is the simplified list)

Place Address City State
Seaside Aquarium N. Prom Seaside OR
Tillamook Air Museum 6030 Hangar Road Tillamook OR
Mariner Square 250 S.W. Bay Blvd. Newport OR
Sea Lion Caves 11 miles north of Florence on U.S Florence OR
Myrtlewood Gallery 1125 Highway 101 Reedsport OR
West Coast Game Park Safari seven miles south of Bandon on U.S. 101 Bandon OR
Prehistoric Gardens between Port Orford and Gold Beach on U.S. 101 Gold Beach OR

After looking over the pictures, she says “Do you think we can we hit them all?”.

She opens up Google maps and starts searching.

“Hang on a second!”, I say. “That’s going to take forever!”.

Ever trying to prove to my wife that programming is cool, I say “Can I see that for a minute?”.

“Watch this”, I say, taking the laptop.

I create a CSV file (attractions.csv) in Vim with the Name, Address, City & State of each of the locations.

(I’m using ruby-1.9.3-p448, if you use Ruby < 1.9.2 you will need the FasterCSV gem to run the code below.)

“I’ll use the Data Science Toolkit to get coordinates for these places”, I tell her. “Then I’ll plot them out on a map”.

She looks skeptical.

I consider explaining how once we have 100 different places on our list, looking them all up on Google will take forever.

Before I can, she just smiles and says, “Let’s see it.”

“I need to have the Data Science Toolkit gem installed”, I tell her. “It’s already installed, but if it weren’t, I would install it like this”

gem install dstk

“Now I have to start up Pry”.

I run ‘pry’ in the command line.

“Next I need the library that will help me pull data from the CSV, so I’ll type:”

require 'csv'

“Then I require the Data Science Toolkit

require 'dstk'

I think about explaining ‘require’ in depth, but I can already tell that I am loosing her.

“Now I need a place to store the stuff I get from the CSV, so I’ll create a variable called ‘locations’”

locations = []

“Then I’ll take each row from the CSV and store them in the variable”

CSV.foreach("attractions.csv", :headers => true) do |csv|
  locations << csv
end

I set up for the DSTK

dstk = DSTK::DSTK.new

“This is where we start having fun”, I say. “For each location (row) I’ll do the following:”

  • “Create a search string”
  • “Get the coordinates from the dstk API”
  • “Add the coordinates to the row object”
  • “Add the latitude to the row object”
  • “Add the longitude to the row object”
locations.each do |l|
  search = "#{l['Address']} #{l['City']} #{l['State']}"
  coordinates = dstk.street2coordinates(search)
  l["coordinates"] = coordinates[search]
  l["lat"] = l["coordinates"]["latitude"]
  l["lng"] = l["coordinates"]["longitude"]
end

She points out that I misspelled ‘locations’. I fix it and continue.

“Now I’ll pull the coordinates out of the row objects and put them into an array”.

place_coordinates = locations.map {|l| [l["lat"],l["lng"]] }

“You just typed ‘map’. Will that create the map?”

“Not exactly”, I say. “I’m building a new list of just coordinates. It looks like this:”

I type:

place_coordinates.inspect

And we see the array of coordinates.

"[[45.99316, -123.92264], [45.419218, -123.80523], [44.630535, -124.052149], [43.974707, -124.101731], [43.702045, -124.107248], [43.168543, -124.350079], [42.420493, -124.418655]]"

Mapping

“Now I’m going to put all those points into a map with little tents as the ic…”.

I shift my eyes to find her fast asleep on the pillow. I think about nudging her, so she can be here for the most exciting part. She looks too comfortable, though, and I can’t bear to wake her up…

So I log in to WordPress and start typing up this post.

The Art of War

Many people have talked about how Sun Tzu’s The Art of War can relate to one’s business and personal life. It’s not the easiest read, but there are definitely some nuggets in there. I found this great little visual synopsis of the book on YouTube.

http://www.youtube.com/watch?v=k2UyGF1G7Xs

Everyone in war can see the tactics, but no one knows the strategy

Benchmarking Ruby’s Sinatra Synchrony and Goliath against Clojure’s Ring

I put together a little comparison today of how Ruby’s Sinatra Synchrony and Goliath libraries stack up against Clojure’s Ring web framework. I haven’t dug into the numbers yet (my wife says it’s time for dinner), but I intend to do it some time soon.

Here is the code: https://github.com/campeterson/benchmark

© 2014 Cam Peterson

Theme by Anders NorenUp ↑