All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
PinDrivernow routes the pad to the GPIO function before setting its direction. A pin whoseIO_MUX.MCU_SELboots on an alternate function - JTAG, SPI flash, UART - stayed on that function, soset_high/set_lownever reached the pad (#585)- Fixed issue 502 - some ADC/RTC pins for esp32c5 and esp32c6 had mapping errors
- Fixed async SPI transactions occasionally hanging forever on multi-core chips
- SPI:
SpiBusDriver::newno longer leaves the device attached to the bus when acquiring the bus lock fails. The device used to stay registered, so freeing the bus later failed withESP_ERR_INVALID_STATE("not all CSses freed") and panicked inSpiDriver's destructor
QueueSet2,QueueSet3,QueueSet4for waiting on multiple heterogeneous queues- Basic compatibility for ESP-IDF release 6.0.
- I2S Standard, PDM and TDM Mode Driver dynamic reconfiguration (change sample rate)
- Support ESP-IDF 6.1 DSI bus flags and continuous high-speed clock-lane configuration.
- I2S:
ClockSource::ApllandClockSource::Xtalonesp32p4, where the XTAL is now the default for this chip - Dropped support for RGB/BGR666 format on ESP-IDF 6.0.
- Fix a bug where the GPIO module did not build for esp32c5
- Fix a bug where the LCD driver was not protected with the esp_lcd component
- Fix several compilation issues with esp32p4 and esp32h2
- MSRV raised to 1.79
- The Timer drivers are not available under ESP-IDF 6+. These need to be rewritten against their newer "gptimer" ESP-IDF C equivalents.
- (#529)
PeripheralandPeripheralRefremoved and replaced with a simple pattern similar to theesp-halone.- Check #529 for details on that change
- The
preludemodule is removed. It was anyway only having a handful of types listed in it. And furthermore, there was nopreludemodule foresp-idf-svc. embassy-syncupdated to V0.7- Implement new pcnt API. The old API will be available through the
pcnt-legacyfeature. ThreadSpawnConfiguration::namechanged from&'static [u8]to&'static core::ffi::CStras it is actually aCStr- The
rmtmodule is now only exposed on chips that support the rmt feature - The
onewirecode has been updated for 1.0.4 and the new rmt API - Implement new gptimer API. The old API will be available through the
timer-legacyfeature. - Update
embedded-io,embedded-io-async, andheaplessdependencies to their latest versions - Raise Rust MSRV to
1.82
DB_11ADC attenuation in favor ofDB_12for ESP-IDF V5.0+- The old pcnt API (only available with the
pcnt-legacyfeature). - The old rmt API (only available with the
rmt-legacyfeature) - The old gptimer API (only available with the
timer-legacyfeature)
SendforAsyncCanDriverDB_12ADC attenuationfade_with_time,fade_with_step,fade_stopforLedcDriverwrite_with_breakforUartDriverandUartTxDriver- New RMT API
- New GPTimer API
- esp32p4 pins and core command added.
- Support for esp32c5 and esp32c61
- Made the modem peripheral for esp32p4 dependent on esp_wifi_remote
- LDO support for esp32p4
- Support for
PLL_F48MUART clock source on ESP32-H2 - LCD driver support; DSI peripheral support with the LCD driver for esp32p4
- New Deep and Light Sleep API (#555)
- Fix pcnt_rotary_encoder example for esp32
- Fix the SDMMC driver for ESP-IDF V5.5+
- Fix UART initialization panic on ESP32-H2 with ESP-IDF 5.x due to unsupported
PLL_F48Mclock source - Replace Arc with Rc in ledc_threads example (#514)
- Fix outdated task docs
- CAN: fix wrong Alert enum indexing / remove wrong TryFromPrimitive derive (#532)
- GPIO: Allow interoperability with other code that initializes the GPIO ISR service (#537)
- SD card support is no longer behind the
experimentalfeature - Added 6th TX channel config argument for IDF 5.5.2+.
- esp32p4 uses vPortExitCriticalMultiCore instead of vPortExitCritical
- UART: Fix dangling driver state when initialization fails mid-way
- Fix ledc example (#511)
- Fix missing sdspi_host_get_dma_info for sdcard driver bug (#509)
- Fix RGB to u32 conversion order in rmt_neopixel example (#505)
- Fix missing sdmmc_host_get_dma_info for sdcard driver bug (#507)
- Make the UART config to ESP-IDF C config reusable outside the UART driver (for OpenThread)
- Re-use the SPI config struct of the SPI driver (for OpenThread)
- Add a
usb_serialperipheral to thePeripheralsstruct for MCUs that do support the USB-serial-jtag peripheral (#503) - UART default baud rate changed from 19'200 to 115'200
- Compatibility with ESP-IDF v5.3.X
- Docs: Add some docstrings for ADC module. (#455)
- Update uart.rs, add rs485 half duplex (#456)
- Update spi.rs, fix half3wire read (#459)
- SPI: Mark DMA as supported in the docu
- SD Card driver; SD Card host drivers (SPI and SDMMC) (#457)
- RMT Onewire Peripheral (#454)
- Add an example of using interrupt directly without async (#474)
- Add support for ESP internal temperature sensor (#337)
- Support the thread modem peripheral on the c6 MCU
- Add dual and quad SPI support (#479)
- Initial USB-serial-jtag driver (#504)
- Fix: UartDriver as embedded_io::Read blocks until buffer is full (#475)
- Allow modem splits even when only HW coex is used
- Fix esp_reset_reason_t constant names (#483)
- Fix drop for AdcDriver in deregister event callbacks (#487)
- Typo fix in task docs (#489)
- Update spi.rs for correct label in gpio_cs capture (#492)
- Fix a crash when dropping
SpiDriver
- The crate now does build with ESP-IDF V4.4.x + esp32c2/esp32c6/esp32h2, yet these MCUs should only be used with ESP-IDF V5+ as they are not officially supported with ESP-IDF V4.4.x (#450)
- Enum
ResetReasonnot dealing with all reset reasons (panics on unknown reset reason) (#443, #444)
ESP-IDF v4.4 Please start upgrading to ESP-IDF v5.
- removed: ESP-IDF v4.3 support, including mostly conditional compilations. (#420)
- GPIO20 is now included on esp32. Notice that not every esp32 has a physical GPIO20. (#370)
- ledc: now it can also be used with high_speed channels on esp32. (#424)
- dependency: updated embassy-sync to 0.6. (#437)
- adc: legacy adc driver is now behind a feature flag
adc-oneshot-legacystarting with >= ESP-IDF v5.0. For justification, look at espressif/esp-idf#13938. (#433) - spi: cs_pre/post_trans_delay() config option included. (#266)
- rmt:
Symbolnow derivesCloneandCopy(#386) - reset: restart() function (#383)
- pcnt: Can now be used with esp32c6. (#407)
- can: Frame flags enum (#411)
- task: added MallocCap enum - Flag to indicate the capabilities of a Memory Region. (#419)
- i2s: new I2sDriver implementation allowing for splited bidirectional i2s. (#435)
- spi: added new_without_sclk SpiDriver constructor (#440)
- esp32h2 builds: added missing hys_ctrl_mode field (#387)
- rmt: FixedLengthSignal was broken, which led to failures in the neopixel/smartled examples, among other things. (#402)
- e-hal 0.2: implements DelayUs and DelayMs for u8 to be consistent with the other implementations (#414)
- i2c: wrong timeout calculations. (#415)
- task: Compatibility with ESP-iDF v5.3 (pre-release) - adding stack_alloc_caps to
ThreadSpawnConfiguration. (#419) - i2s: Compatibility with ESP-IDF v5.3 (pre-release) - small internal adjustments to i2s (#419)
- ledc: max_duty() method miscalulated in certain conditions. (#431)
- timer: fix clock usage in timer driver used with ESP-IDF v5.x (#441)
- i2c: use correct xtal for esp32c2 on timeout calculations. (#438)
- Fix - PinDriver state changes and the drop call invoked pull-ups to be enabled. New default behavior on init / state transition / drop is to not enable pull-ups. (#344). If users want to reduce power usage on unused pins, they now need to manually enable pull-ups on a pin. For example, call
core::mem::forgeton the PinDriver instance after setting the pull-ups. - #354 - breaking change -
rmtdriver now does not directly exposermt_item32_tbut rather - wraps it with aSymbolnewtype - #374 - Improve delay/timer errors and rounding calculations
- #379 - Remove 4096 limit for SPI dma transfer size
- Fix clippy duplicate imports warnings with latest 1.78 nightly
- Breaking change: feature
riscv-ulp-hal(and consequently, featureesp-idf-sys) is now removed. Use the esp-ulp-riscv-hal crate instead - MSRV 1.75; remove the nightly feature flag from all async trait implementations
- Update public dependencies
e-halto 1.0.0 andembassy-syncto 0.5 and private dependencyheaplessto 0.8 - Allow
cargo check --all-featuresto work correctly (there used to be a build error when both theesp-idf-sysand theriscv-ulp-halfeatures were enabled) - #365 - Async Uart driver is not
Send - #362 - Async Uart write hangs forever
- #353 - Use the
PLL_F80Mclock source with ESP IDF 5.1 and esp32c6 - #351 - Remove the
OutputPinrequirement from SPI SDI pin - #350 - Not checking for ESP_FAIL in AsyncCanDriver::transmit
- BREAKING CHANGE IN A PATCH RELEASE DUE TO DISCOVERED UB: The
subscribemethods in driversPinDriver,PcntDriverandTimerDriverno longer accept non-static callbacks, as these lead to UB / crash when the driver is forgotten with e.g.core::mem::forget. Since local borrows are a very useful feature however, these are still allowed via the newly-introduced and even more unsafesubscribe_nonstaticmethod.
- Remove dependency on
AtomicU64which is no longer supported by the upstream*-espidftargets - Fix some Clippy warnings in the
spidriver
- Fix Timer array index bug #331 - prevented the use of TIMER10 on devices that support only 2 Timers
- Fix wrong TIMER11 index definition that declared TIMER11 as TIMER10 #331
- Support for latest ESP IDF 5.2 dev (master)
- Fix ambiguous name error #325 - a compilation issue when the NimBLE component is enabled in
esp-idf-sys - Fix compilation issues of the I2S driver for esp32h2 and esp32c2
- Fix compilation issues of the ADC drivers when the ESP IDF
esp_adccomponent is not enabled - Fix compilation issues of the GPIO driver for esp32c6
- MSRV raised to 1.71
- New driver: I2S
- New driver: continuous ADC
- New driver: snapshot ADC, implementing the new ESP-IDF 5.0+ snapshot ADC API
- Async support in the following drivers: Timer, SPI, I2S, continuous ADC, CAN (via an
AsyncCanDriverwrapper), UART (via anAsyncUartDriverwrapper) - All async drivers can now work out of the box with any executor (
edge-executorno longer a necessity) via a new ISR-to-task bridge (esp_idf_hal::interrupt::asynch::IsrReactor) - CAN driver: support for pulling alerts in blocking and async mode
- UART driver: support for pulling UART events
- GPIO driver: scoped callback;
subscribecallback now needs to live only as long as thePinDriverinstance - Timer driver: scoped callback;
subscribecallback now needs to live only as long as theTimerDriverinstance taskandinterruptmodules: new submodule in each -asynch- featuring a signal/notification-like synchronization primitivetaskmodule:block_onmethod capable of executing a future on the current threadtaskmodule: new sub-module -queuefor the FreeRTOSqueuesynchonization primitivetaskmodule: new sub-module -notification- a more ergonomic API around the FreeRTOS task notification API which was already exposed via thetask::notifyandtask::wait_notificationAPIs- Upgraded to
embedded-hal1.0.0-rc.1 andembedded-hal-async1.0.0-rc.1 - Dependency
esp-idf-sysnow re-exported asesp_idf_hal::sys - Breaking change:
Peripherals::takenow returns an error when the peripherals are already taken - Breaking change:
delay::Delaystruct extended with configurable threshold - Breaking change:
task::wait_any_notificationremoved;task::notifyrenamed totask::notify_and_yield; new function -task::notify- that notifies a task without automatically yielding to the notified task if it is a higher priority than the currently interrupted one - Breaking change:
task::notify*andtask::waitnow take/returnNonZeroU32instead ofu32 - Breaking change: GPIO - interrupts are now disabled automatically each time an ISR is triggered so as to avoid the IWDT triggering on level interrupts; use has to re-enable - in non-ISR code - via
PinDriver::enable_interrupt() - Breaking change: ESP IDF support for
edge-executormoved to theedge-executorcrate - Deprecated: Using ESP-IDF 4.3 is now deprecated and all special cfg flags will be removed in the next release
- Do not set the single shot flag in the CAN frame (#263)
- Add safe abstractions to CRC functions in ESP ROM (#261)
- Use same error for all e-hal 0.2 trait impls (#260)
- Add support for WakeupReason from Ext0 events (#259)
- Compilation failed when ESP_IDF_VERSION = "release/v5.1" (#258)
- Fix UART docs (#252)
- UART driver was broken - see #250 for more details
- Removed a forgotten
[patch.crates-io]section that was usingesp-idf-sysfrommaster(low impact, only relevant when building the examples) - Clippy fixes
- MSRV 1.66 (but MSRV 1.70 necessary if
nightlyis enabled) - Support for new chips: esp32c2, esp32h2, esp32c6 and future proofed for esp32c5 and esp32p4
- Support for ESP IDF 5.0, 5.1 and 5.2 (master)
- Support for
embedded-hal1.0-alpha.10 andembedded-hal-async0.2.0-alpha.2 (API breakage in i2c and spi) - Async GPIO native API with support for
embedded-hal-async - PCNT driver
- Alerts and Mode support in the TWAI driver
- Task Watchdog API
- Configurable interrupt levels in all drivers (minor API breakage in
SpiDriver) EspError::from_infallible- Auto-reload support in the timer driver
Fix the build when the ULP peripheral is enabled.
Rebase on top of esp-idf-sys 0.32:
- Retire any usages of
esp-idf-sys::c_typesin favor ofcore::ffi - Remove casts from
usizetou32and back now thatesp-idf-sysis compiled with--size_t-is-usizeenabled
Minor doc fixes; Clippy fixes; CriticalSection will panic on FreeRTOS mutex lock/unlock failures.
Patch releases to fix compilation errors under no_std.
Release 0.39 is a backwards-incompatible release where almost all drivers were touched in one way or another.
The main themes of the 0.39 release are:
- Restore drop semantics for all drivers
- Simpler driver types (little to no generics)
- Unified naming
- Public API
- Completely revamped GPIO metaphor
- Timer driver
- Support for the
critical-sectioncrate by providing aCriticalSectionimplementation - Support for the
embassy-synccrate by providing two types of raw mutexes - Support for the
edge-executorcrate - Modem and Mac peripherals
- SPI driver rework
- The
release()method is now retired everywhere - Just dropping a driver will make it stop functioning
- If peripherals need to be reused after the driver is dropped, this is achieved by passing the peripherals to the driver constructor via
&mutreferences, e.g.I2cMasterDriver::new(&mut peripherals.i2c1, &mut peripherals.pins.gpio10, &mut peripherals.pins.gpio11, &mut peripherals.pins.gpio12)
- All peripheral generics are removed from all drivers' types; e.g., the I2C master driver now has the following type signature:
I2cMasterDriver<'d> - The lifetime -
'dfrom above designates the lifetime of the peripheral (e.g.I2c) that is used by the driver - In the most common case where the peripheral is just moved into the driver (and no longer accessible when the driver is dropped),
'd='static, or in other words, the type signature of the driver becomesI2cMasterDriver<'static>and can even be type aliased by the user as inpub type SimpleI2cMasterDriver = I2cMasterDriver<'static>; - When the peripherals are passed to the driver using
&mutreferences,'ddesignates the lifetime of the&mutreference - Note that the constructor of each driver is still generic by the peripherals it takes, but these generics are "erased" from the type of the driver once the driver is constructed, as we only need to "remember" whether the peripheral is moved into the driver, or used via a
&mutreference and can be reused after the driver is dropped. This semantics is captured by the'dlifetime
- Each driver is named
<Peripheral>Driver, where<Peripheral>is the name of the peripheral. E.g. the driver for theUartperipheral is namedUartDriver, the driver for theAdcperipheral is namedAdcDriverand so on - The one exception is the GPIO subsystem, where the driver is not named
GpioDriver, but the simplerPinDriverinstead, even if the concrete peripheral struct type might be e.g.Gpio12 - In case there are multiple drivers per peripheral - as in - say - a master and a slave protocol driver - only the
Slavecontains itself in its name, as in e.g.SpiDriverandSpiSlaveDriver - NOTE: We are NOT fond of still using offensive terms like "master" and "slave". Unfortunately, the embedded industry itself has not yet agreed (to our knowledge) on a good replacement for these terms, hence they are still around
In addition to implementing the embedded-hal traits where possible, all drivers now have public API. While the public API loosely follows the APIs from embedded-hal, it deviates where appropriate so that the native underlying ESP IDF driver is better utilized.
These public APIs mean that the user is no longer required to depend on the embedded-hal crate so as to consume the esp-idf-hal drivers.
Consuming the drivers via the embedded-hal traits is now only necessary when the user is targetting cross-platform portability of their application code.
In previous releases, the GPIO driver was a bit different from the others, in that each pin peripheral itself used to have TypeState and methods that configured the peripheral into a certain mode (as in e.g. input-only, input-output, output-only, ADC and so on) that drived the peripheral through a type-state change.
Furthermore, there was no notion of a pin "driver". The driver methods were fused directly into the peripheral, and a subset of these methods were available, depending on the type-state of the peripheral.
This scheme was asymmetric with the rest of the HAL, where peripherals are mostly opaque singleton objects, and it is the driver that exposes a user visible API (including implementation of the corresponding embedded-hal traits). It also suffered from less flexibility and code duplication accross the regular GPIO pins, and their degraded Any*Pin variants.
The new GPIO subsystem follows the driver-peripheral separation metaphor like everything else:
- Pin peripherals are static objects with no type-state, where the only allowed operation is to degrade a concrete pin instance implementing the
Pin/InputPin/OutputPintraits into a correspondingAny*Pinstruct which is useful when e.g. passing an array of input or output pins accross the application code, as generics are erased by the degrading operation - The capabilities of a pin peripheral are expressed by the concrete pin struct type (i.e.
GpioXX) implementing a set of traits (InputPin,AnalogPin) and so on PinDriveris the universal driver for any pin peripheral (e.g. be it a non-degradedGpioXXstruct, or a degradedAnyIOPin,AnyOutputPinorAnyInputpin struct)- Providing an ISR handler is possible - via
PinDriver- on any input pin, including on a degraded one
Finally, PinDriver can now work with digital pins by using the RTC digital IO API on chips that support this (ESP32 and the ESP32 S* series)
The new timer module is exposing the hardware timer peripherals on the esp32* MCUs via the ESP-IDF timer driver. Just like the underlying ESP-IDF timer driver, a lot (but not all)
methods are actually safe to call from an ISR routine. Those which are unsafe will panic immediately if called from an ISR.
In addition to implementing embedded-hal crates, esp-idf-hal integrates with the wider Rust embedded ecosystem by providing out of the box critical section implementation for the critical-section crate.
To enable this support, you need to compile the crate with the critical-section feature enabled.
Note that the provided critical section implementation is based on esp_idf_hal::task::CriticalSection, which itself is based on a recursive FreeRtos mutex.
This means that you can enter() the critical section for long periods of time, and your code can still be preempted by ISR routines or high priority FreeRtos tasks (threads), thus you should not worry about slowing down the whole RTOS reaction times by using a critical section.
This also means however, that this critical section (and its native esp_idf_hal::task::CriticalSection equivalent) CANNOT be used from an ISR context.
If you need a critical section with a warranty that your code will not be interrupted by an ISR or higher priority tasks (i.e. working accross ISRs and tasks), you should be using
esp_idf_hal::interrupt::IsrCriticalSection instance, or the esp_idf_hal::interrupt::free method. Keep in mind that you should stay in this type of critical section for a very short period of time, because it disables all interrupts.
The HAL provides two mutex implementations that implement the RawMutex trait from the embassy-sync crate thus integrating the ESP-RS ecosystem better with the Rust embedded async ecosystem:
EspRawMutex- this implementation uses a FreeRtos mutex and behaves similarly toesp_idf_hal::task::CriticalSectionIsrRawMutex- this implementation works by disabling all interrupts and thus behaves similarly toesp_idf_hal::interrupt::IsrCriticalSection
To enable this support, you need to compile the crate with the embassy-sync feature enabled.
While embassy-sync itself can use - via its CriticalSectionRawMutex mutex bridge - whatever a HAL is providing as a critical section to the critical-section crate, the above two implementations provide further benefits:
EspRawMutexis almost the same as theembassy-syncnativeCriticalSectionRawMutexwith the one major difference that ALLCriticalSectionRawMutexinstances in fact share a single RTOS mutex underneath. This is a limitation which is coming from thecritical-sectioncrate itself. In contrast, eachEspRawMutexinstance has its own separate RTOS mutexIsrRawMutex- as the name suggests - allowsembassy-syncto be utilized in scenarios where the application code should synchronize accross ISRs and regular RTOS task based code
This is another HAL feature that integrates the ESP-RS ecosystem with the Rust embedded async ecosystem.
The edge-executor crate is a simple local-only executor that is based on the popular async-task crate coming from the Smol async executor (which in turn powers the async-std executor).
Without getting into too many details, edge-executor is useful in embedded cotexts because
- It is
no_stdcompatible (but requires an allocator, unlikeembassy-executor, so the latter might be a better fit for bare-metal use cases where ESP IDF is not utilized) - Has a small footprint
- Is ISR-friendly in that it can operate from an ISR context - or - which would be the natural setup when used on top of a RTOS like ESP IDF's FreeRtos - can be awoken directly from an ISR routine, this having a minimal latency
To enable this support, you need to compile the crate with the edge-executor feature enabled.
The support for edge-executor in esp-idf-hal is in terms of providing an ISR-friendly Monitor trait implementation for edge-executor - FreeRtosMonitor - that can be awoken directly from an ISR, and is based on the FreeRtos task notification mechanism which is exposed in the esp_idf_hal::task crate.
In previous releases, the Wifi and Ethernet drivers (part of the esp-idf-svc crate) did not follow the philosophy of esp-idf-hal in that each driver should take a corresponding peripheral (by moving or by a &mut reference).
This is now addressed in that the esp-idf-hal crate models two new peripherals:
modem::Modem: models the esp32* hardware modem peripheral and implements two new marker traits:modem::WifiModemPeripheralandmodem::BluetoothModemPeripheral- By implementing both of the above traits, the
modem::Modemperipheral should be usable by both the Wifi driver inesp-idf-svc, as well as the future Bluetooth driver modem::Modemis splittable into two other peripherals:modem::WifiModem(implementing only themodem::WifiModemPeripheraltrait) andmodem::BluetoothModem(implementing only themodem::BluetoothModemPeripheraltrait) so that in future the simultaneous operation of the Wifi and Bluetooth drivers to be possible
- By implementing both of the above traits, the
mac::MAC: models the EMAC hardware available on the original ESP32 chip. The Ethernet driver implementation - just like the Wifi driver implementation - is still in theesp-idf-svccrate which better aligns with the underlying ESP-IDF implementations, yet the new Wifi and Ethernet drivers inesp-idf-svcnow expect their corresponding peripherals to be supplied during construction time
The SPI driver is now split into two structures
SpiDriver- represents an initialized SPI bus and manages access to the underlying SPI hardwareSpiDeviceDriver(new) - an abstraction for rach device connected to the SPI bus
The above split allows for the creation of more than one device per SPI bus. (Up to 6 for the esp32c* variants and 3 for all others).
When creating an SpiDeviceDriver instance, user is required to provide a Borrow to the SpiDriver instance (as in SpiDriver, &SpiDriver, &mut SpiDriver, Rc(SpiDriver) or Arc(SpiDriver)), so that the SPI bus stays initialized throughout the liftime of all devices.
A second wrapper implementation is now also provided: SpiSoftCsDeviceDriver It allows for more devices per SPI bus (i.e. above the 3/6 limit). This is implemented by operating the CS pin in software mode.