ESP8266 esp_timer Uses a FreeRTOS Tick Contract, Not ESP32 Microsecond Scheduling
A component can compile against esp_timer on ESP8266 and still have a different timing contract from its ESP32 version. In ESP8266 RTOS SDK v3.4, the microsecond argument does not establish a microsecond-resolution callback scheduler. A rejected start request and a slowly drifting periodic callback can both follow directly from the compatibility implementation.
This review fixes v3.4 to commit 89a3f254b63819035f65d9c5dcdae8864f1a6a8a. It concerns legacy firmware maintenance, not a claim of current vendor support. Espressif's maintenance advisory should be evaluated separately from API compatibility. No firmware build or target timing measurement was performed.
Read the local warning, not the inherited introduction
The ESP8266 timer header contains an introductory description mentioning ESP32 hardware and microsecond accuracy. Later, an ESP8266-specific warning says this implementation is based on a FreeRTOS timer and exists for compatibility. The ISR dispatch option is commented out.
That combination is a practical migration hazard. An editor can show a familiar function declaration while the implementation beneath it has changed. Review the linked implementation and effective configuration before preserving the timing promises of a portable component. A successful build only establishes that the interface was available.

Original implementation diagram. The intervals are conceptual, not a captured timing trace.
Check exact divisibility before starting the timer
Both start functions in the pinned implementation calculate a quantum of 1000 × (1000 / CONFIG_FREERTOS_HZ) microseconds. A nonzero remainder or a zero tick count returns ESP_ERR_INVALID_ARG. The value is not silently rounded to the nearest supported interval.
The FreeRTOS configuration defaults to 100 Hz and says the selected rate must divide 1000. At that default, the quantum is 10,000 microseconds. A 15,000-microsecond request fails; 20,000 passes this arithmetic gate. At 1000 Hz, the quantum becomes 1000 microseconds, so a 1500-microsecond request still fails.
These examples are source-derived arithmetic, not measured periods. Inspect the project's generated configuration instead of assuming its tick rate equals the default. Record the requested interval and return code together. Otherwise an unchecked start failure can look like an intermittent sensor, a stalled worker or an unrelated watchdog problem.
Avoid repairing the failure by rounding without a requirement decision. Rounding a debounce interval upward may be acceptable; changing a control deadline or communications timeout may not be. Give the application an explicit supported interval range and reject configurations outside it before starting normal operation.
Periodic means rearming this particular wrapper
The implementation creates a FreeRTOS timer with auto-reload disabled. After invoking the application callback, its periodic state calls xTimerReset. The next interval is therefore tied to that rearm operation rather than an independent, continuously maintained hardware phase.
The SDK's timer command contract describes reset timing relative to the reset command and separates command admission from processing by the daemon. This explains why merely measuring the interval passed to esp_timer_start_periodic misses an important dependency: the previous callback has to return before the wrapper requests another expiration.
For an illustrative 100-millisecond request with a callback consuming 7 milliseconds, do not assume a 100-millisecond phase-locked sequence. Tick quantization, rearm timing and daemon scheduling all matter. It would also be unjustified to promise exactly 107 milliseconds without measuring the actual execution. Preserve callback-entry and callback-exit timestamps separately when testing that hypothesis.
Choose the repair around the real requirement
For housekeeping where approximate repetition is acceptable, keep the callback short and hand off bounded work. Specify whether another request can merge with pending work. Moving a network operation to a worker reduces callback occupancy, but it does not turn the wrapper into a phase-locked hardware scheduler.
If a task must follow a periodic release schedule, evaluate the selected port's task-delay-until mechanism and define its missed-period policy. That is a scheduling design change, not an interchangeable function rename. If the requirement concerns physical output edges, first select an appropriate hardware timing mechanism.
The ESP8266 hardware-timer reference describes a separate peripheral API, including ISR callbacks and different allowed alarm ranges for one-shot and reload operation. Moving there introduces interrupt-context, resource-ownership and latency obligations. ESP32 timer-group or newer esp_timer dispatch examples are not an ESP8266 implementation recipe.
Qualify the boundary, then the timing
Prepare a small acceptance matrix around zero, one quantum, a nonmultiple, and several supported multiples. Check actual return codes first. Then compare callback entries under a short callback, deliberately longer bounded work and representative competing activity. Keep the test workload safe and outside production control paths.
For periodic operation, compare observations with both hypotheses: a preserved absolute phase and a delay beginning around rearm. Keep the configuration, SDK commit and callback workload with the trace. Also confirm that unsupported intervals fail visibly rather than leaving the rest of the application waiting forever.
The release decision should state which timing semantics the product requires and which mechanism supplies them. A microsecond-shaped API argument is insufficient evidence. The important result is a verified contract between configuration, interval admission, rearming and the work that consumes the callback.