# CloudAMQP with MQTT and C

We recommend the [Eclipse Paho C client](https://github.com/eclipse-paho/paho.mqtt.c). The examples use its synchronous `MQTTClient` API. On Debian and Ubuntu, install it with:

```bash
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`:

```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`:

```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](https://github.com/cloudamqp/examples/tree/main/c/paho-mqtt).

## 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.
