πIntroduction
Timer event callbacks in Mongoose may be used in certain scenarios, such as:
- Sending heartbeat messages to connected clients
- Implementing reconnection logic for MQTT or WebSocket clients
- Regularly uploading telemetry data
- Regularly checking for software update versions
- …
Its functionality and usage methods should be familiar to everyone, which essentially involves writing a callback function –> initializing the event manager –> adding the function to the timer list of the event manager.
πTimer API
πmg_timer_init
void mg_timer_init(struct mg_timer **head, struct mg_timer *t, uint64_t period_ms, unsigned flags, void (*fn)(void *), void *fn_data);
πmg_timer_poll
void mg_timer_poll(struct mg_timer **head, uint64_t uptime_ms);
πmg_timer_free
void mg_timer_free(struct mg_timer **head, struct mg_timer *t);
These interfaces are used for initializing, polling, and freeing timers, which are automatically handled internally by the time manager, so we generally do not need to manage them manually. Therefore, the timer interface is quite simple, with only one function: mg_timer_add();
πmg_timer_add
struct mg_timer { uint64_t period_ms; // Timer period in milliseconds uint64_t expire; // Expiration timestamp in milliseconds unsigned flags; // Possible flags values below #define MG_TIMER_ONCE 0 // Call function once #define MG_TIMER_REPEAT 1 // Call function periodically #define MG_TIMER_RUN_NOW 2 // Call immediately when timer is set #define MG_TIMER_CALLED 4 // Timer function was called at least once #define MG_TIMER_AUTODELETE 8 // mg_free() timer when done void (*fn)(void *); // Function to call void *arg; // Function argument struct mg_timer *next; // Linkage};struct mg_timer *mg_timer_add(struct mg_mgr *mgr, uint64_t period_ms, unsigned flags, void (*fn)(void *), void *fn_data);
Add the timer callback function to the timer list of the event manager.
Parameters:mgr Event manager;
Parameters:period_ms Millisecond interval;
Parameters:flags Timer flag mask, as follows:
#When the timer expires, the callback function fn will be executed only once and then automatically stop.
MG_TIMER_ONCE
# The timer will repeatedly execute the callback function fn at the interval specified by period_ms until explicitly stopped or freed.
MG_TIMER_REPEAT
# Immediately execute the callback function. If this flag is set, the timer will immediately trigger the callback function fn once when started, and then continue executing according to the rules of MG_TIMER_ONCE or MG_TIMER_REPEAT.
MG_TIMER_RUN_NOW
#Automatically set by the system to indicate whether the callback function has been executed at least once.
MG_TIMER_CALLED
# Automatically set by the system to free its own memory when the timer expires or stops. This flag is automatically added when the timer is created.
MG_TIMER_AUTODELETE
Parameters:fn Callback function;
Parameters:fn_data Callback function argument;
Returns: A pointer to the created timer.
Note: It is essential to ensure that the polling interval of the event manager mg_mgr_poll() is less than the timer; otherwise, the timer call may be missed. This is an important consideration in the use of software timers.
πCase Demonstration
#include <stdio.h>#include <string.h>#include <stdint.h>#include <time.h>#include <sys/time.h>#include "mongoose.h"struct mg_timer *mytimer;// If this handle is not needed, it can be omitted// System time print static void SysTimePrint(void){ struct timeval tv; struct tm *tm_info; char time_str[20]; // Store "HH:MM:SS" char ms_str[4]; // Store milliseconds (3 digits + '\0') gettimeofday(&tv, NULL); // Get time (seconds + microseconds) tm_info = localtime(&tv.tv_sec); // Convert to local time // Format time: hours:minutes:seconds strftime(time_str, sizeof(time_str), "%H:%M:%S", tm_info); // Extract milliseconds part (microseconds / 1000) snprintf(ms_str, sizeof(ms_str), "%03ld", tv.tv_usec / 1000); printf("Current time: %s.%s\n", time_str, ms_str); // Output format: HH:MM:SS.mmm}static void timer_fn(void *arg) { SysTimePrint(); printf("Timer function called\r\n\r\n");}int main(int argc, char *argv[]){ /* Create and initialize the event management structure */ struct mg_mgr mgr; mg_mgr_init(&mgr); /* Create a timer and add it to the specified event management structure */ mytimer = mg_timer_add(&mgr, 3000, MG_TIMER_REPEAT|MG_TIMER_RUN_NOW, timer_fn, &mgr); /* Loop to process events */ SysTimePrint(); while(1){ mg_mgr_poll(&mgr, 1000); } /* Close connections and free resources */ mg_mgr_free(&mgr); exit(0);}

From the above case, we can see that even with the MG_TIMER_RUN_NOW flag set, the callback function is triggered only after 1 second from the program start, which is the polling time we set for the time manager. This characteristic may need to be noted in certain scenarios.
It is also possible to change the period within the callback function:
static void timer_fn(void *arg) { SysTimePrint(); printf("Timer function called\r\n\r\n"); mytimer->period_ms = 1000;}

πConclusion
- Although the timers in Mongoose are not highly accurate, they are generally sufficient. To improve accuracy, the polling cycle of the event manager should also be adjusted accordingly. However, if set too fast, it will increase CPU usage, requiring a balance.
- To implement your own timer, it is not feasible to execute Mongoose-related APIs within the callback unless using a multithreading mechanism, but this does not improve time accuracy, making it meaningless.
- The timer interfaces in Mongoose can be used in other programs, meaning you can manually call mg_timer_init, mg_timer_poll, mg_timer_free, etc., which is equivalent to implementing your own software timer. Of course, there are many open-source implementations of software timers, and if the only purpose is to achieve this, introducing the Mongoose library would be counterproductive, especially since other open-source implementations may be simpler, more efficient, and easier to understand. Therefore, it is generally not recommended to use these interfaces in programs unrelated to Mongoose.