| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- #pragma once
- // Minimal Arduino.h substitute for compiling GxEPD2 under ESP-IDF. GxEPD2's
- // vendored source calls down into a small, fixed set of Arduino primitives
- // (digitalWrite/pinMode/digitalRead on plain int16_t pin numbers, plus
- // delay/micros/yield) - this shim backs them with esphome::GPIOPin via
- // PinRegistry, so GxEPD2 compiles completely unmodified against it.
- #include <cstdint>
- #include <sstream>
- #include <string>
- #include <esp_task_wdt.h>
- #include "esphome/core/hal.h"
- #include "esphome/core/log.h"
- #include "esphome/components/gxepd2/pin_registry.h"
- #ifndef ESP32
- #define ESP32
- #endif
- #define HIGH 1
- #define LOW 0
- #define INPUT 0
- #define OUTPUT 1
- #define INPUT_PULLUP 2
- inline void pinMode(int16_t pin, uint8_t mode) {}
- inline void digitalWrite(int16_t pin, uint8_t value) {
- if (auto *p = esphome::gxepd2::PinRegistry::get(pin))
- p->digital_write(value != 0);
- }
- inline int digitalRead(int16_t pin) {
- auto *p = esphome::gxepd2::PinRegistry::get(pin);
- return (p != nullptr && p->digital_read()) ? HIGH : LOW;
- }
- // GxEPD2_EPD::_waitWhileBusy() spins on these two (delay() in its main
- // branch, yield() right after) for as long as the busy pin stays asserted -
- // deliberately up to tens of seconds (_busy_timeout), since a real 3/4-color
- // refresh can legitimately take that long. Neither esphome::delay() nor
- // esphome::yield() resets the ESP-IDF task watchdog on their own, so
- // without an explicit reset here, a real (or a misconfigured-polarity, thus
- // permanently "busy") wait outlasts the watchdog and the whole device
- // reboots mid-refresh instead of the loop's own timeout ever getting a
- // chance to fire.
- //
- // This calls esp_task_wdt_reset() directly, not esphome::App.feed_wdt():
- // feed_wdt()'s rate-limiting state is global, shared by every caller, on
- // the assumption only the main loop task ever calls it. This code runs on
- // gxepd2's own task (see gxepd2_display.h), so the main loop's frequent
- // feed_wdt() calls kept satisfying that shared rate limit and silently
- // swallowing this task's - esp_task_wdt_reset() always resets whichever
- // task is actually calling it, isn't rate-limited, and is cheap enough to
- // call unconditionally here.
- inline void delay(uint32_t ms) {
- esp_task_wdt_reset();
- esphome::delay(ms);
- }
- inline uint32_t micros() { return esphome::micros(); }
- inline void yield() {
- esp_task_wdt_reset();
- esphome::yield();
- }
- #define pgm_read_byte(addr) (*reinterpret_cast<const uint8_t *>(addr))
- // GxEPD2's per-transfer timing diagnostics are opt-in (this component never
- // enables them, by passing serial_diag_bitrate=0 to init()) and stay
- // discarded here. But a handful of unconditional warnings - notably
- // GxEPD2_EPD::_waitWhileBusy()'s "Busy Timeout!" - also go through Serial,
- // and those matter: they're the only signal that the busy pin never
- // released. Buffer print() calls and flush to the logger on println(), so
- // that warning actually surfaces instead of vanishing silently.
- class GxEPD2SerialShim {
- public:
- void begin(uint32_t baud) {}
- template<typename T> void print(T value) { this->buffer_ << value; }
- template<typename T> void println(T value) {
- this->buffer_ << value;
- this->flush_();
- }
- void println() { this->flush_(); }
- private:
- void flush_() {
- std::string line = this->buffer_.str();
- if (!line.empty())
- ESP_LOGW("gxepd2", "%s", line.c_str());
- this->buffer_.str("");
- this->buffer_.clear();
- }
- std::ostringstream buffer_;
- };
- inline GxEPD2SerialShim Serial; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|