diff options
Diffstat (limited to 'analysis/rtt')
| -rw-r--r-- | analysis/rtt/README.txt | 40 | ||||
| -rw-r--r-- | analysis/rtt/src/logger.c | 151 | ||||
| -rw-r--r-- | analysis/rtt/src/logger.h | 22 | ||||
| -rw-r--r-- | analysis/rtt/src/main.c | 40 | ||||
| -rw-r--r-- | analysis/rtt/src/mqtt_client.c | 114 | ||||
| -rw-r--r-- | analysis/rtt/src/mqtt_client.h | 29 |
6 files changed, 286 insertions, 110 deletions
diff --git a/analysis/rtt/README.txt b/analysis/rtt/README.txt new file mode 100644 index 0000000..4a45e6d --- /dev/null +++ b/analysis/rtt/README.txt | |||
| @@ -0,0 +1,40 @@ | |||
| 1 | Project: MQTT RTT and Throughput Logger | ||
| 2 | |||
| 3 | Description: | ||
| 4 | This project implements an MQTT client that connects to a broker, subscribes to a topic, and logs message round-trip time (RTT) and throughput statistics to a CSV file and standard output. The logger measures the time difference between message sent timestamps and received timestamps to calculate RTT and maintains a sliding window of message times to estimate throughput in messages per second. | ||
| 5 | |||
| 6 | Files: | ||
| 7 | |||
| 8 | mqtt_client.h / mqtt_client.c: MQTT client wrapper using the Mosquitto library. | ||
| 9 | |||
| 10 | logger.h / logger.c: Logger module for handling incoming MQTT messages and logging RTT/throughput. | ||
| 11 | |||
| 12 | main.c: Entry point initializing the MQTT client and logger, then running the MQTT client loop. | ||
| 13 | |||
| 14 | Dependencies: | ||
| 15 | |||
| 16 | Mosquitto library (libmosquitto) | ||
| 17 | |||
| 18 | Standard C libraries (stdio, stdlib, string, sys/time, time) | ||
| 19 | |||
| 20 | Build Instructions: | ||
| 21 | |||
| 22 | Look at the Dockerfile and Makefile to find out. | ||
| 23 | Usage: | ||
| 24 | |||
| 25 | Configure MQTT broker address, port, and topic in main.c. | ||
| 26 | |||
| 27 | Run the compiled executable. | ||
| 28 | |||
| 29 | The program connects to the broker, subscribes to the specified topic, and logs RTT and throughput stats to a CSV file. | ||
| 30 | |||
| 31 | Output is also printed to the console. | ||
| 32 | |||
| 33 | Notes: | ||
| 34 | |||
| 35 | The logger expects the payload of received messages to contain a timestamp string representing the sent time in milliseconds. | ||
| 36 | |||
| 37 | The program uses blocking call mosquitto_loop_forever to run the MQTT network loop. | ||
| 38 | |||
| 39 | Contact: | ||
| 40 | For questions contact Filip Wandzio at contact@philw.dev | ||
diff --git a/analysis/rtt/src/logger.c b/analysis/rtt/src/logger.c index ed5f6e5..e1dd2f3 100644 --- a/analysis/rtt/src/logger.c +++ b/analysis/rtt/src/logger.c | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | |||
| 2 | #include "logger.h" | 1 | #include "logger.h" |
| 3 | #include <stdio.h> | 2 | #include <stdio.h> |
| 4 | #include <stdlib.h> | 3 | #include <stdlib.h> |
| @@ -9,78 +8,120 @@ | |||
| 9 | #define WINDOW_SEC 1 | 8 | #define WINDOW_SEC 1 |
| 10 | #define MAX_TIMES 1000 | 9 | #define MAX_TIMES 1000 |
| 11 | 10 | ||
| 11 | #define ISO_TIME_BUF_SIZE 32 | ||
| 12 | |||
| 13 | #define MICROSECONDS_IN_SECOND 1000000 | ||
| 14 | #define MICROSECONDS_IN_MILLISECOND 1000 | ||
| 15 | |||
| 16 | #define CSV_HEADER "timestamp,sent_ms,received_ms,rtt_ms,throughput_msg_per_s\n" | ||
| 17 | #define CSV_LINE_FORMAT "%s,%lld,%lld,%lld,%d\n" | ||
| 18 | |||
| 12 | static FILE *csv_file = NULL; | 19 | static FILE *csv_file = NULL; |
| 13 | static double msg_times[MAX_TIMES]; | 20 | static double msg_times[MAX_TIMES]; |
| 14 | static int msg_count = 0; | 21 | static int msg_count = 0; |
| 15 | 22 | ||
| 16 | static long long current_time_ms() { | 23 | /** |
| 17 | struct timeval tv; | 24 | * @brief Returns the current time in milliseconds since the Epoch. |
| 18 | gettimeofday(&tv, NULL); | 25 | * |
| 19 | return ((long long)tv.tv_sec * 1000) + (tv.tv_usec / 1000); | 26 | * @return Current time in milliseconds (int64). |
| 27 | */ | ||
| 28 | static long long current_time_ms(void); | ||
| 29 | |||
| 30 | /** | ||
| 31 | * @brief Returns the current time in seconds (with microsecond precision) since | ||
| 32 | * the Epoch. | ||
| 33 | * | ||
| 34 | * @return Current time in seconds (double). | ||
| 35 | */ | ||
| 36 | static double current_time_sec(void); | ||
| 37 | |||
| 38 | /** | ||
| 39 | * @brief Updates the sliding window of message timestamps and returns the count | ||
| 40 | * of messages within WINDOW_SEC. | ||
| 41 | * | ||
| 42 | * @param now Current timestamp in seconds. | ||
| 43 | * @return Number of messages in the last WINDOW_SEC seconds. | ||
| 44 | */ | ||
| 45 | static int update_throughput_window(double now); | ||
| 46 | |||
| 47 | static long long current_time_ms(void) | ||
| 48 | { | ||
| 49 | struct timeval tv; | ||
| 50 | gettimeofday(&tv, NULL); | ||
| 51 | return ((long long)tv.tv_sec * 1000) + | ||
| 52 | (tv.tv_usec / MICROSECONDS_IN_MILLISECOND); | ||
| 20 | } | 53 | } |
| 21 | 54 | ||
| 22 | static double current_time_sec() { | 55 | static double current_time_sec(void) |
| 23 | struct timeval tv; | 56 | { |
| 24 | gettimeofday(&tv, NULL); | 57 | struct timeval tv; |
| 25 | return (double)tv.tv_sec + (tv.tv_usec / 1e6); | 58 | gettimeofday(&tv, NULL); |
| 59 | return (double)tv.tv_sec + | ||
| 60 | (tv.tv_usec / (double)MICROSECONDS_IN_SECOND); | ||
| 26 | } | 61 | } |
| 27 | 62 | ||
| 28 | static int update_throughput_window(double now) { | 63 | static int update_throughput_window(double current_time) |
| 29 | int i, new_count = 0; | 64 | { |
| 30 | for (i = 0; i < msg_count; i++) { | 65 | int msg_index, window_count = 0; |
| 31 | if (now - msg_times[i] < WINDOW_SEC) { | 66 | for (msg_index = 0; msg_index < msg_count; msg_index++) { |
| 32 | msg_times[new_count++] = msg_times[i]; | 67 | if (current_time - msg_times[msg_index] < WINDOW_SEC) |
| 33 | } | 68 | msg_times[window_count++] = msg_times[msg_index]; |
| 34 | } | 69 | } |
| 35 | msg_times[new_count++] = now; | 70 | msg_times[window_count++] = current_time; |
| 36 | msg_count = new_count; | 71 | msg_count = window_count; |
| 37 | return msg_count; | 72 | return msg_count; |
| 38 | } | 73 | } |
| 39 | 74 | ||
| 40 | void logger_init(const char *filename) { | 75 | void logger_init(const char *filename) |
| 41 | csv_file = fopen(filename, "w"); | 76 | { |
| 42 | if (!csv_file) { | 77 | csv_file = fopen(filename, "w"); |
| 43 | perror("Cannot open CSV file"); | 78 | if (!csv_file) { |
| 44 | exit(EXIT_FAILURE); | 79 | perror("Cannot open CSV file"); |
| 45 | } | 80 | exit(EXIT_FAILURE); |
| 46 | fprintf(csv_file, | 81 | } |
| 47 | "timestamp,sent_ms,received_ms,rtt_ms,throughput_msg_per_s\n"); | 82 | fprintf(csv_file, CSV_HEADER); |
| 48 | fflush(csv_file); | 83 | fflush(csv_file); |
| 49 | } | 84 | } |
| 50 | 85 | ||
| 51 | void logger_cleanup() { | 86 | void logger_cleanup(void) |
| 52 | if (csv_file) { | 87 | { |
| 53 | fclose(csv_file); | 88 | if (csv_file) { |
| 54 | csv_file = NULL; | 89 | fclose(csv_file); |
| 55 | } | 90 | csv_file = NULL; |
| 91 | } | ||
| 56 | } | 92 | } |
| 57 | 93 | ||
| 58 | void logger_handle_message(const void *payload, int payloadlen) { | 94 | void logger_handle_message(const void *payload, int payloadlen) |
| 59 | char *payload_str = malloc(payloadlen + 1); | 95 | { |
| 60 | if (!payload_str) | 96 | if (payloadlen <= 0) |
| 61 | return; | 97 | return; |
| 62 | memcpy(payload_str, payload, payloadlen); | 98 | |
| 63 | payload_str[payloadlen] = '\0'; | 99 | char *payload_str = malloc(payloadlen + 1); |
| 100 | if (!payload_str) | ||
| 101 | return; | ||
| 102 | |||
| 103 | memcpy(payload_str, payload, payloadlen); | ||
| 104 | payload_str[payloadlen] = '\0'; | ||
| 64 | 105 | ||
| 65 | long long sent_ms = atoll(payload_str); | 106 | long long sent_ms = atoll(payload_str); |
| 66 | free(payload_str); | 107 | free(payload_str); |
| 67 | 108 | ||
| 68 | long long received_ms = current_time_ms(); | 109 | long long received_ms = current_time_ms(); |
| 69 | long long rtt = received_ms - sent_ms; | 110 | long long rtt = received_ms - sent_ms; |
| 70 | 111 | ||
| 71 | double now_sec = current_time_sec(); | 112 | double now_sec = current_time_sec(); |
| 72 | int throughput = update_throughput_window(now_sec); | 113 | int throughput = update_throughput_window(now_sec); |
| 73 | 114 | ||
| 74 | time_t now = time(NULL); | 115 | time_t now = time(NULL); |
| 75 | struct tm *tm_info = localtime(&now); | 116 | struct tm *tm_info = localtime(&now); |
| 76 | char iso_time[32]; | 117 | char iso_time[ISO_TIME_BUF_SIZE]; |
| 77 | strftime(iso_time, sizeof(iso_time), "%Y-%m-%dT%H:%M:%S", tm_info); | 118 | strftime(iso_time, sizeof(iso_time), "%Y-%m-%dT%H:%M:%S", tm_info); |
| 78 | 119 | ||
| 79 | if (csv_file) { | 120 | if (csv_file) { |
| 80 | fprintf(csv_file, "%s,%lld,%lld,%lld,%d\n", iso_time, sent_ms, received_ms, | 121 | fprintf(csv_file, CSV_LINE_FORMAT, iso_time, sent_ms, |
| 81 | rtt, throughput); | 122 | received_ms, rtt, throughput); |
| 82 | fflush(csv_file); | 123 | fflush(csv_file); |
| 83 | } | 124 | } |
| 84 | 125 | ||
| 85 | printf("RTT: %lld ms | Throughput: %d msg/s\n", rtt, throughput); | 126 | printf("RTT: %lld ms | Throughput: %d msg/s\n", rtt, throughput); |
| 86 | } | 127 | } |
diff --git a/analysis/rtt/src/logger.h b/analysis/rtt/src/logger.h index f1602c9..904c327 100644 --- a/analysis/rtt/src/logger.h +++ b/analysis/rtt/src/logger.h | |||
| @@ -1,9 +1,25 @@ | |||
| 1 | |||
| 2 | #ifndef LOGGER_H | 1 | #ifndef LOGGER_H |
| 3 | #define LOGGER_H | 2 | #define LOGGER_H |
| 4 | 3 | ||
| 4 | /** | ||
| 5 | * @brief Initializes the logger by opening the CSV file and writing the header. | ||
| 6 | * | ||
| 7 | * @param filename Path to the CSV log file. | ||
| 8 | */ | ||
| 5 | void logger_init(const char *filename); | 9 | void logger_init(const char *filename); |
| 6 | void logger_cleanup(); | 10 | |
| 11 | /** | ||
| 12 | * @brief Closes the CSV file and cleans up resources. | ||
| 13 | */ | ||
| 14 | void logger_cleanup(void); | ||
| 15 | |||
| 16 | /** | ||
| 17 | * @brief Processes an incoming message payload, parses the timestamp, | ||
| 18 | * calculates RTT and throughput, and logs this data to the CSV and stdout. | ||
| 19 | * | ||
| 20 | * @param payload Pointer to the message payload. | ||
| 21 | * @param payloadlen Length of the payload in bytes. | ||
| 22 | */ | ||
| 7 | void logger_handle_message(const void *payload, int payloadlen); | 23 | void logger_handle_message(const void *payload, int payloadlen); |
| 8 | 24 | ||
| 9 | #endif // LOGGER_H | 25 | #endif |
diff --git a/analysis/rtt/src/main.c b/analysis/rtt/src/main.c index 13f7716..163b5f6 100644 --- a/analysis/rtt/src/main.c +++ b/analysis/rtt/src/main.c | |||
| @@ -1,4 +1,3 @@ | |||
| 1 | |||
| 2 | #include "logger.h" | 1 | #include "logger.h" |
| 3 | #include "mqtt_client.h" | 2 | #include "mqtt_client.h" |
| 4 | #include <stdio.h> | 3 | #include <stdio.h> |
| @@ -8,21 +7,26 @@ | |||
| 8 | #define TOPIC "device/echo/in" | 7 | #define TOPIC "device/echo/in" |
| 9 | #define CSV_FILE "rtt_throughput_log.csv" | 8 | #define CSV_FILE "rtt_throughput_log.csv" |
| 10 | 9 | ||
| 11 | int main() { | 10 | /** |
| 12 | mqtt_client_t client; | 11 | * @brief Entry point for MQTT client program. |
| 13 | 12 | * | |
| 14 | logger_init(CSV_FILE); | 13 | * Initializes the logger and MQTT client, then enters the MQTT loop to receive |
| 15 | 14 | * messages. Cleans up resources on exit. | |
| 16 | if (mqtt_client_init(&client, BROKER_ADDRESS, BROKER_PORT, TOPIC) != 0) { | 15 | * |
| 17 | fprintf(stderr, "MQTT initialization error\n"); | 16 | * @return int Exit code (0 on success, 1 on failure). |
| 18 | return 1; | 17 | */ |
| 19 | } | 18 | int main(void) |
| 20 | 19 | { | |
| 21 | printf("Connection established.Waiting for messages...\n"); | 20 | mqtt_client_t client; |
| 22 | mqtt_client_loop(&client); | 21 | logger_init(CSV_FILE); |
| 23 | 22 | ||
| 24 | logger_cleanup(); | 23 | if (mqtt_client_init(&client, BROKER_ADDRESS, BROKER_PORT, TOPIC)) |
| 25 | mqtt_client_cleanup(&client); | 24 | return fprintf(stderr, "MQTT initialization error\n"), 1; |
| 26 | 25 | ||
| 27 | return 0; | 26 | printf("Connection established. Waiting for messages...\n"); |
| 27 | mqtt_client_loop(&client); | ||
| 28 | logger_cleanup(); | ||
| 29 | mqtt_client_cleanup(&client); | ||
| 30 | |||
| 31 | return 0; | ||
| 28 | } | 32 | } |
diff --git a/analysis/rtt/src/mqtt_client.c b/analysis/rtt/src/mqtt_client.c index 43a06d8..edc394d 100644 --- a/analysis/rtt/src/mqtt_client.c +++ b/analysis/rtt/src/mqtt_client.c | |||
| @@ -3,46 +3,98 @@ | |||
| 3 | #include <stdio.h> | 3 | #include <stdio.h> |
| 4 | #include <stdlib.h> | 4 | #include <stdlib.h> |
| 5 | 5 | ||
| 6 | #define WINDOW_SEC 1 | 6 | #define MQTT_KEEPALIVE_SECONDS 60 |
| 7 | #define MQTT_QOS_LEVEL 0 | ||
| 8 | #define LOOP_TIMEOUT_MS -1 | ||
| 9 | #define LOOP_MAX_MESSAGES 1 | ||
| 7 | 10 | ||
| 11 | /** | ||
| 12 | * @brief Callback called when a message arrives on a subscribed topic. | ||
| 13 | * | ||
| 14 | * @param mosq Mosquitto client instance. | ||
| 15 | * @param userdata User data pointer (unused). | ||
| 16 | * @param msg Incoming message data. | ||
| 17 | */ | ||
| 8 | static void on_message(struct mosquitto *mosq, void *userdata, | 18 | static void on_message(struct mosquitto *mosq, void *userdata, |
| 9 | const struct mosquitto_message *msg); | 19 | const struct mosquitto_message *message); |
| 20 | |||
| 21 | /** | ||
| 22 | * @brief Helper function to handle errors by printing a message, | ||
| 23 | * cleaning up the MQTT client, and returning failure. | ||
| 24 | * | ||
| 25 | * @param client MQTT client instance. | ||
| 26 | * @param error_message Error message to print. | ||
| 27 | * @param mosq_error_code Mosquitto error code. | ||
| 28 | * @return int Always returns 1 to indicate failure. | ||
| 29 | */ | ||
| 30 | static int fail_with_cleanup(mqtt_client_t *client, const char *error_message, | ||
| 31 | int mosq_error_code); | ||
| 10 | 32 | ||
| 11 | int mqtt_client_init(mqtt_client_t *client, const char *broker_address, | 33 | int mqtt_client_init(mqtt_client_t *client, const char *broker_address, |
| 12 | int port, const char *topic) { | 34 | int broker_port, const char *topic) |
| 13 | mosquitto_lib_init(); | 35 | { |
| 14 | client->mosq = mosquitto_new(NULL, true, NULL); | 36 | if (!client || !broker_address || !topic) |
| 15 | if (!client->mosq) { | 37 | return fprintf(stderr, "Invalid MQTT init parameters\n"), 1; |
| 16 | fprintf(stderr, "Cannot create MQTT client\n"); | 38 | |
| 17 | return 1; | 39 | int init_result = mosquitto_lib_init(); |
| 18 | } | 40 | if (init_result != MOSQ_ERR_SUCCESS) { |
| 19 | 41 | fprintf(stderr, "Mosquitto library initialization failed: %s\n", | |
| 20 | mosquitto_message_callback_set(client->mosq, on_message); | 42 | mosquitto_strerror(init_result)); |
| 21 | 43 | return 1; | |
| 22 | if (mosquitto_connect(client->mosq, broker_address, port, 60) != | 44 | } |
| 23 | MOSQ_ERR_SUCCESS) { | 45 | |
| 24 | fprintf(stderr, "Nie można połączyć się z brokerem MQTT\n"); | 46 | client->mosq = mosquitto_new(NULL, true, NULL); |
| 25 | return 1; | 47 | if (!client->mosq) |
| 26 | } | 48 | return fprintf(stderr, "Cannot create MQTT client\n"), 1; |
| 27 | 49 | ||
| 28 | if (mosquitto_subscribe(client->mosq, NULL, topic, 0) != MOSQ_ERR_SUCCESS) { | 50 | mosquitto_message_callback_set(client->mosq, on_message); |
| 29 | fprintf(stderr, "Cannot subscribe to topic\n"); | 51 | |
| 30 | return 1; | 52 | int connect_result = mosquitto_connect( |
| 31 | } | 53 | client->mosq, broker_address, broker_port, MQTT_KEEPALIVE_SECONDS); |
| 32 | 54 | if (connect_result != MOSQ_ERR_SUCCESS) | |
| 33 | return 0; | 55 | return fail_with_cleanup( |
| 56 | client, "Cannot connect to MQTT broker", connect_result); | ||
| 57 | |||
| 58 | int subscribe_result = | ||
| 59 | mosquitto_subscribe(client->mosq, NULL, topic, MQTT_QOS_LEVEL); | ||
| 60 | if (subscribe_result != MOSQ_ERR_SUCCESS) | ||
| 61 | return fail_with_cleanup(client, "Cannot subscribe to topic", | ||
| 62 | subscribe_result); | ||
| 63 | |||
| 64 | return 0; | ||
| 34 | } | 65 | } |
| 35 | 66 | ||
| 36 | void mqtt_client_cleanup(mqtt_client_t *client) { | 67 | void mqtt_client_cleanup(mqtt_client_t *client) |
| 37 | mosquitto_destroy(client->mosq); | 68 | { |
| 38 | mosquitto_lib_cleanup(); | 69 | if (!client) |
| 70 | return; | ||
| 71 | |||
| 72 | if (client->mosq) | ||
| 73 | mosquitto_destroy(client->mosq); | ||
| 74 | |||
| 75 | mosquitto_lib_cleanup(); | ||
| 39 | } | 76 | } |
| 40 | 77 | ||
| 41 | void mqtt_client_loop(mqtt_client_t *client) { | 78 | void mqtt_client_loop(mqtt_client_t *client) |
| 42 | mosquitto_loop_forever(client->mosq, -1, 1); | 79 | { |
| 80 | if (client && client->mosq) | ||
| 81 | mosquitto_loop_forever(client->mosq, LOOP_TIMEOUT_MS, | ||
| 82 | LOOP_MAX_MESSAGES); | ||
| 43 | } | 83 | } |
| 44 | 84 | ||
| 45 | static void on_message(struct mosquitto *mosq, void *userdata, | 85 | static void on_message(struct mosquitto *mosq, void *userdata, |
| 46 | const struct mosquitto_message *msg) { | 86 | const struct mosquitto_message *message) |
| 47 | logger_handle_message(msg->payload, msg->payloadlen); | 87 | { |
| 88 | if (message && message->payload) | ||
| 89 | logger_handle_message(message->payload, message->payloadlen); | ||
| 90 | } | ||
| 91 | |||
| 92 | static int fail_with_cleanup(mqtt_client_t *client, const char *error_message, | ||
| 93 | int mosq_error_code) | ||
| 94 | { | ||
| 95 | fprintf(stderr, "%s: %s\n", error_message, | ||
| 96 | mosquitto_strerror(mosq_error_code)); | ||
| 97 | if (client && client->mosq) | ||
| 98 | mosquitto_destroy(client->mosq); | ||
| 99 | return 1; | ||
| 48 | } | 100 | } |
diff --git a/analysis/rtt/src/mqtt_client.h b/analysis/rtt/src/mqtt_client.h index 781e742..1faa4ad 100644 --- a/analysis/rtt/src/mqtt_client.h +++ b/analysis/rtt/src/mqtt_client.h | |||
| @@ -1,16 +1,39 @@ | |||
| 1 | |||
| 2 | #ifndef MQTT_CLIENT_H | 1 | #ifndef MQTT_CLIENT_H |
| 3 | #define MQTT_CLIENT_H | 2 | #define MQTT_CLIENT_H |
| 4 | 3 | ||
| 5 | #include <mosquitto.h> | 4 | #include <mosquitto.h> |
| 6 | 5 | ||
| 7 | typedef struct { | 6 | typedef struct { |
| 8 | struct mosquitto *mosq; | 7 | struct mosquitto *mosq; |
| 9 | } mqtt_client_t; | 8 | } mqtt_client_t; |
| 10 | 9 | ||
| 10 | /** | ||
| 11 | * @brief Initialize the MQTT client, connect to the broker, and subscribe to a | ||
| 12 | * topic. | ||
| 13 | * | ||
| 14 | * @param client Pointer to mqtt_client_t struct. | ||
| 15 | * @param broker_address MQTT broker IP or hostname. | ||
| 16 | * @param port Broker port number. | ||
| 17 | * @param topic Topic to subscribe to. | ||
| 18 | * @return int 0 on success, 1 on failure. | ||
| 19 | */ | ||
| 11 | int mqtt_client_init(mqtt_client_t *client, const char *broker_address, | 20 | int mqtt_client_init(mqtt_client_t *client, const char *broker_address, |
| 12 | int port, const char *topic); | 21 | int port, const char *topic); |
| 22 | |||
| 23 | /** | ||
| 24 | * @brief Cleanup MQTT client resources and free associated memory. | ||
| 25 | * | ||
| 26 | * @param client Pointer to mqtt_client_t struct. | ||
| 27 | */ | ||
| 13 | void mqtt_client_cleanup(mqtt_client_t *client); | 28 | void mqtt_client_cleanup(mqtt_client_t *client); |
| 29 | |||
| 30 | /** | ||
| 31 | * @brief Run the MQTT client loop to process network events. | ||
| 32 | * | ||
| 33 | * This function blocks indefinitely. | ||
| 34 | * | ||
| 35 | * @param client Pointer to mqtt_client_t struct. | ||
| 36 | */ | ||
| 14 | void mqtt_client_loop(mqtt_client_t *client); | 37 | void mqtt_client_loop(mqtt_client_t *client); |
| 15 | 38 | ||
| 16 | #endif // MQTT_CLIENT_H | 39 | #endif |
