MQTT, or message queue telemetry transport, is a protocol for device communication that Adafruit IO supports. Using a MQTT library or client you can publish and subscribe to a feed to send and receive feed data.
If you aren't familiar with MQTT check out this introduction from the HiveMQ blog. All of the subsequent posts in the MQTT essentials series are great and worth reading too.
To use the MQTT API that Adafruit IO exposes you'll need a MQTT client library. For Python, Node.js, and Arduino you can use Adafruit's IO client libraries as they include support for MQTT (see the client libraries section). For other languages or platforms look for a MQTT library that ideally supports the MQTT 3.1.1 protocol.
Connection Details
You will want to use the following details to connect a MQTT client to Adafruit IO:
- Host: io.adafruit.com
- Port: 1883 or 8883 (for SSL encrypted connection)
- Username: your Adafruit account username (see the accounts.adafruit.com page here to find yours)
- Password: your Adafruit IO key (click the AIO Key button on a dashboard to find the key)
We strongly recommend using SSL if your MQTT client allows it.
If the MQTT library requires that you set a client ID then use a unique value like a random GUID. Most MQTT libraries handle setting the client ID to a random value automatically though.
Topics
Adafruit IO's MQTT API exposes feed data using special topics. You can publish a new value for a feed to its topic, or you can subscribe to a feed's topic to be notified when the feed has a new value. Any one of the following topic forms is valid for a feed:
- (username)/feeds/(feed name or key)
- (username)/f/(feed name or key)
Where (username) is your Adafruit IO username (the same as specified when connecting to the MQTT server) and (feed name or key) is the feed's name or key. The smaller '/f/' path is provided as a convenience for small embedded clients that need to save memory.
Check out our guide to Feed Naming for the full details.
For example if your username is mosfet and you're accessing a feed called Photocell One (which has a Key of photocell-one) you can use any of these paths:
- mosfet/feeds/Photocell One
- mosfet/f/Photocell One
- mosfet/feeds/photocell-one
- mosfet/f/photocell-one
To append a new value to a feed perform a MQTT publish against the feed path and provide the new feed value as the payload of the request.
To be notified of a change in a feed perform a MQTT subscribe against the feed path. When a new value is added to the feed then the Adafruit IO MQTT server will send a notification with the new feed value in the payload.
You can also subscribe to the parent 'feeds' path to be notified when any owned feed changes using MQTT's #
wildcard character. For example the mosfet user could subscribe to either:
- mosfet/feeds/#
- mosfet/f/#
Once subscribed to the path above any change to a feed owned by mosfet will be sent to the MQTT client. The topic will specify the feed that was updated, and the payload will have the new value.
Be aware the MQTT server sends feed updates on all possible paths for a specific feed. For example, subscribing to mosfet/f/#
and publishing to mosfet/f/photocell-one
would produce messages from: mosfet/f/photocell-one, mosfet/f/photocell-one/json, and mosfet/f/photocell-one/csv; each referring to the same updated value. To reduce noise, make sure to grab the specific topic of the feed / format you're interested in and change your subscription to that.
PLEASE NOTE: as we adjust which identifiers we use for Feeds internally, the feed updates you receive when using a wildcard will include but may not be limited to the ones shown above.
If you'd like to avoid the formatted feeds ("/json" and "/csv" topics) but still see all the feeds you're publishing to, you can use MQTT's +
wildcard in place of #
. In this case, subscribing to mosfet/f/+
would produce output on mosfet/f/photocell-one
, but not mosfet/f/photocell-one/json
.
Publish QoS Levels
One feature of MQTT is the ability to specify a QoS, or quality of service, level when publishing feed data. This allows an application to confirm that its data has been sucessfully published. If you aren't familiar with MQTT QoS levels be sure to read this great blog post explaining their meaning.
For publishing feed values the Adafruit IO MQTT API supports QoS level 0 (at most once) and 1 (at least once) only. QoS level 2 (exactly once) is not currently supported.
Rate Limit
Adafruit IO's MQTT server imposes a rate limit to prevent excessive load on the service. If a user performs too many publish actions in a short period of time then some of the publish requests might be rejected. The current rate limit is at most 1 request per second (or 60 requests within 60 seconds).
If you exceed this limit, a notice will be sent to the (username)/throttle topic. You can subscribe to the topic if you wish to know when the Adafruit IO rate limit has been exceeded for your user account.
This limit applies to all connections so if you have multiple devices or clients publishing data be sure to delay their updates enough that the total rate is below 2 requests/second.
Data Format
There are a few ways to send data to our MQTT API if you're writing your own client library.
The simplest way to send values to an IO Feed topic is to just send the value. For example, a temperature sensor is going to produce numeric values like 22.587
. If you're sending to mosfet/feeds/photocell-one
you can send using a number data type or a string data type. That means either 22.587
or "22.587"
will be accepted and stored as a numeric value.
Adafruit IO does its best to treat data as numeric values so that we can show you your data as a chart on an Adafruit IO dashboard and through our Charting API.
Data with Location
To tag your data with a location value, you'll either need to wrap it in a JSON object first or send it to the special /csv formatted MQTT topic.
Sending JSON
JSON can be sent to either the base topic or the /json topic-- for example, mosfet/feeds/photocell-one
or mosfet/feeds/photocell-one/json
. The proper format for location tagged JSON data is:
{
"value": 22.587,
"lat": 38.1123,
"lon": -91.2325,
"ele": 112
}
Specifically, JSON objects must include a "value" key, and may include "lat", "lon", and "ele" keys.
Sending CSV
Alternatively, you can send location tagged data to /csv topics. In this example that would be the topic mosfet/feeds/photocell-one/csv
instead of mosfet/feeds/photocell-one
. Both store data in the same feed. The format IO expects for location tagged CSV data is VALUE, LATITUDE, LONGITUDE, ELEVATION.
With the example data shown before, that means you could publish the string "22.587,38.1123,-91.2325,112"
to mosfet/feeds/photocell-one/csv
. to store the value "22.587" in the location latitude: 38.1123, longitude: -91.2325, elevation: 112.
Examples
Using a simple Ruby MQTT library and the data shown, all these examples publish the same data to the same feed:
# first you'll need https://github.com/njh/ruby-mqtt require 'mqtt' username = 'test_username' key = 'not-a-real-key' url = "mqtts://#{ username }:#{ key }@io.adafruit.com" mqtt_client = MQTT::Client.connect(url, 8883) # simplest thing that could possibly work mqtt_client.publish('test_username/feeds/example', 22.587) # sending numbers as strings is fine, IO stores all data internally # as strings anyways mqtt_client.publish('test_username/feeds/example', '22.587') # CSV formatted, no location mqtt_client.publish('test_username/feeds/example/csv', '22.587') # CSV formatted, with location mqtt_client.publish('test_username/feeds/example/csv', '22.587,38.1123,-91.2325,112') # JSON formatted, no location mqtt_client.publish('test_username/feeds/example', '{"value":22.587}') mqtt_client.publish('test_username/feeds/example/json', '{"value":22.587}') # JSON formatted, with location mqtt_client.publish('test_username/feeds/example', '{"value":22.587,"lat":38.1123,"lon":-91.2325,"ele":112}') mqtt_client.publish('test_username/feeds/example/json', '{"value":22.587,"lat":38.1123,"lon":-91.2325,"ele":112}')
Sending JSON data through Adafruit IO
Because Adafruit IO supports additional features beyond a basic MQTT brokering service, such as location tagging for data points, the service supports data in the JSON format described above. Namely:
{
"value": 22.587,
"lat": 38.1123,
"lon": -91.2325,
"ele": 112
}
This lets us store the individual value, 22.587
, and data about the value: its latitude, longitude, and elevation. Metadata!
But what happens when the value you want to send is itself JSON? Good news! There are a few solutions available to you in that situation.
IO formatted JSON
The simplest way to send JSON data to Adafruit IO is to wrap it in the format described above. For example, if instead of 22.587
, I wanted to send something like, {"sensor-1":22.587,"sensor-2":13.182}
, the "wrapped" version would look like this:
{
"value": {"sensor-1":22.587,"sensor-2":13.182},
"lat": 38.1123,
"lon": -91.2325,
"ele": 112
}
It's worth noting that because Adafruit IO parses the entire JSON object that you send it, any valid JSON will be parsed and when it is stored in our system and forwarded to any subscribers, it will be regenerated. The significance of that is that if you publish JSON data with whitespace, it will be stored and republished without whitespace, because our generator produces the most compact JSON format possible.
Double encoded JSON strings
The second way you can send JSON data as a value is to "double encode" it before sending, in which case IO will treat it as a raw string. If you're using something like javascript's JSON.stringify
function or Ruby's JSON.generate
, double encoding means passing the result of JSON.stringify
through JSON.stringify
a second time. In this node.js console example, you can see the difference:
> JSON.stringify({"sensor-1":22.587,"sensor-2":13.182})
'{"sensor-1":22.587,"sensor-2":13.182}'
> JSON.stringify(JSON.stringify({"sensor-1":22.587,"sensor-2":13.182}))
'"{\"sensor-1\":22.587,\"sensor-2\":13.182}"'
The double encoded JSON string can be sent directly through Adafruit IO without interference from our processing system, because the processing system will not interpret it as JSON. In your receiving code, because the value passed through includes surrounding double quotes, you have to call your parse function twice to restore the JSON object.
> var input = '"{\\\"sensor-1\\\":22.587,\\\"sensor-2\\\":13.182}"'
> JSON.parse(JSON.parse(input))
{ 'sensor-1': 22.587, 'sensor-2': 13.182 }
Non-IO formatted JSON
The third way you can send raw JSON data is to just send it. If Adafruit IO doesn't find a "value" key in the JSON object you send, it will treat the whole blob as plain text and store and forward the data. That means with our example JSON object, sending the string {"sensor-1":22.587,"sensor-2":13.182}
will result in {"sensor-1":22.587,"sensor-2":13.182}
being stored in IO and sent to MQTT subscribers.
Text editor powered by tinymce.