#pragma once #include #include #include "Config.h" #include "Core.h" #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) #define OPTICAL_USE_RMT_DMA 1 #include #else #define OPTICAL_USE_RMT_DMA 0 #include #endif class PulseReceiver { public: bool begin(); bool start(uint32_t expectedHz); void stop(); void resetStream(); size_t readPeriods(PulsePeriod *periods, size_t capacity, TickType_t waitTicks = 0); bool overflowed(); uint32_t takeDroppedItems(); uint32_t tickHz() const; uint16_t receiveChunkSymbols() const { return receiveChunkSymbols_; } bool highRateBackend() const { #if OPTICAL_USE_RMT_DMA && CONFIG_IDF_TARGET_ESP32S3 return true; #else return false; #endif } private: struct Edge { uint32_t tick; uint8_t rising; }; bool consumeEdge(const Edge &edge, PulsePeriod &period); #if OPTICAL_USE_RMT_DMA static constexpr size_t BLOCK_SYMBOLS = RMT_MAX_RECEIVE_SYMBOLS; struct SymbolBlock { uint16_t count; rmt_symbol_word_t symbols[BLOCK_SYMBOLS]; }; static bool IRAM_ATTR onRmt(rmt_channel_handle_t, const rmt_rx_done_event_data_t *, void *); bool nextRmtEdge(Edge &edge, TickType_t waitTicks); rmt_channel_handle_t channel_ = nullptr; rmt_symbol_word_t receiveBuffer_[RMT_MAX_RECEIVE_SYMBOLS]; uint16_t receiveChunkSymbols_ = 0; SymbolBlock isrBlock_ = {}; SymbolBlock block_ = {}; uint16_t blockIndex_ = 0; uint8_t phase_ = 0; bool haveLevel_ = false; bool level_ = false; uint32_t rmtTick_ = 0; #else static void IRAM_ATTR onGpio(void *ctx); uint32_t cpuTickHz_ = 0; #endif QueueHandle_t queue_ = nullptr; volatile bool overflow_ = false; volatile uint32_t droppedItems_ = 0; bool running_ = false; bool haveRise_ = false, haveFall_ = false, haveRawTick_ = false; uint32_t lastRawTick_ = 0; uint64_t tickEpoch_ = 0, rise_ = 0, fall_ = 0; };