CloudAMQP with MQTT and C

We recommend the Eclipse Paho C client. The examples use its synchronous MQTTClient API. On Debian and Ubuntu, install it with:

sudo apt-get install libpaho-mqtt-dev

Publish and subscribe

Set MQTT_HOST, MQTT_PORT, MQTT_USERNAME and MQTT_PASSWORD to the MQTT details on your instance’s details page in the CloudAMQP console. Port 8883 uses TLS. Without them, the examples connect to a local broker.

Save the publisher as publisher.c:

#include <MQTTClient.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static const char* env(const char* name, const char* fallback) {
  const char* value = getenv(name);
  return value ? value : fallback;
}

int main(void) {
  const char* port = env("MQTT_PORT", "1883");
  int tls = strcmp(port, "8883") == 0;
  char uri[256];
  snprintf(uri, sizeof uri, "%s://%s:%s", tls ? "ssl" : "tcp", env("MQTT_HOST", "localhost"), port);
  char client_id[32];
  snprintf(client_id, sizeof client_id, "publisher-%d", (int)getpid());

  MQTTClient client;
  MQTTClient_create(&client, uri, client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
  MQTTClient_connectOptions options = MQTTClient_connectOptions_initializer;
  options.username = env("MQTT_USERNAME", "guest");
  options.password = env("MQTT_PASSWORD", "guest");
  MQTTClient_SSLOptions ssl = MQTTClient_SSLOptions_initializer;
  ssl.verify = 1;  // Paho C checks the certificate, but not its hostname, by default
  if (tls) options.ssl = &ssl;
  int rc = MQTTClient_connect(client, &options);
  if (rc != MQTTCLIENT_SUCCESS) {
    fprintf(stderr, "Connection failed: %s\n", MQTTClient_strerror(rc));
    return 1;
  }

  // QoS 1, and waitForCompletion waits for the broker's PUBACK
  const char* message = "Hello world!";
  MQTTClient_deliveryToken token;
  MQTTClient_publish(client, "hello", (int)strlen(message), message, 1, 0, &token);
  rc = MQTTClient_waitForCompletion(client, token, 10000);
  if (rc != MQTTCLIENT_SUCCESS) {
    fprintf(stderr, "Publish failed: %s\n", MQTTClient_strerror(rc));
    return 1;
  }
  printf(" [x] Sent '%s'\n", message);

  MQTTClient_disconnect(client, 1000);
  MQTTClient_destroy(&client);
  return 0;
}

Save the subscriber as worker.c:

#include <MQTTClient.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static const char* env(const char* name, const char* fallback) {
  const char* value = getenv(name);
  return value ? value : fallback;
}

int main(void) {
  // Print each line straight away, also when the output isn't a terminal
  setvbuf(stdout, NULL, _IOLBF, 0);
  const char* port = env("MQTT_PORT", "1883");
  int tls = strcmp(port, "8883") == 0;
  char uri[256];
  snprintf(uri, sizeof uri, "%s://%s:%s", tls ? "ssl" : "tcp", env("MQTT_HOST", "localhost"), port);
  char client_id[32];
  snprintf(client_id, sizeof client_id, "worker-%d", (int)getpid());

  MQTTClient client;
  MQTTClient_create(&client, uri, client_id, MQTTCLIENT_PERSISTENCE_NONE, NULL);
  MQTTClient_connectOptions options = MQTTClient_connectOptions_initializer;
  options.username = env("MQTT_USERNAME", "guest");
  options.password = env("MQTT_PASSWORD", "guest");
  MQTTClient_SSLOptions ssl = MQTTClient_SSLOptions_initializer;
  ssl.verify = 1;  // Paho C checks the certificate, but not its hostname, by default
  if (tls) options.ssl = &ssl;
  int rc = MQTTClient_connect(client, &options);
  if (rc != MQTTCLIENT_SUCCESS) {
    fprintf(stderr, "Connection failed: %s\n", MQTTClient_strerror(rc));
    return 1;
  }

  MQTTClient_subscribe(client, "hello", 1);
  printf(" [*] Waiting for messages. To exit press CTRL+C\n");
  // Paho C acks each QoS 1 message as it arrives
  for (;;) {
    char* topic;
    int topic_length;
    MQTTClient_message* message;
    rc = MQTTClient_receive(client, &topic, &topic_length, &message, 1000);
    if (rc != MQTTCLIENT_SUCCESS) {
      fprintf(stderr, "Connection lost: %s\n", MQTTClient_strerror(rc));
      return 1;
    }
    if (message) {
      printf(" [x] Received '%.*s'\n", message->payloadlen, (char*)message->payload);
      MQTTClient_freeMessage(&message);
      MQTTClient_free(topic);
    }
  }
}

Build both with cc -o publisher publisher.c -lpaho-mqtt3cs and cc -o worker worker.c -lpaho-mqtt3cs, then run ./worker in one terminal and ./publisher in another.

A runnable example is in c/paho-mqtt in cloudamqp/examples.

Good to know

  • Put the vhost in the username, as vhost:user. MQTT has no vhost of its own, and the console shows the username that way.
  • Link with paho-mqtt3cs, the synchronous library with TLS.
  • Set verify = 1 in the SSL options. Paho C checks the server certificate against the system’s certificates, but by default not that it matches the hostname.
  • Use QoS 1, so MQTTClient_waitForCompletion waits for the broker’s PUBACK. Paho C acks each incoming QoS 1 message as it arrives.
  • Paho C doesn’t reconnect with the synchronous API. When MQTTClient_receive fails, connect and subscribe again.