1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-15 15:18:16 +00:00

Merge branch 'dev' into vornado-ir

This commit is contained in:
Jordan Zucker 2025-02-11 10:27:57 -08:00
commit 08360487b5
18 changed files with 448 additions and 63 deletions

View File

@ -18,8 +18,8 @@ namespace esphome {
namespace http_request { namespace http_request {
struct Header { struct Header {
const char *name; std::string name;
const char *value; std::string value;
}; };
// Some common HTTP status codes // Some common HTTP status codes

View File

@ -96,7 +96,7 @@ std::shared_ptr<HttpContainer> HttpRequestArduino::start(std::string url, std::s
container->client_.setUserAgent(this->useragent_); container->client_.setUserAgent(this->useragent_);
} }
for (const auto &header : headers) { for (const auto &header : headers) {
container->client_.addHeader(header.name, header.value, false, true); container->client_.addHeader(header.name.c_str(), header.value.c_str(), false, true);
} }
// returned needed headers must be collected before the requests // returned needed headers must be collected before the requests

View File

@ -84,7 +84,7 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
container->set_secure(secure); container->set_secure(secure);
for (const auto &header : headers) { for (const auto &header : headers) {
esp_http_client_set_header(client, header.name, header.value); esp_http_client_set_header(client, header.name.c_str(), header.value.c_str());
} }
const int body_len = body.length(); const int body_len = body.length();

View File

@ -25,6 +25,15 @@ void ImageDecoder::draw(int x, int y, int w, int h, const Color &color) {
} }
} }
DownloadBuffer::DownloadBuffer(size_t size) : size_(size) {
this->buffer_ = this->allocator_.allocate(size);
this->reset();
if (!this->buffer_) {
ESP_LOGE(TAG, "Initial allocation of download buffer failed!");
this->size_ = 0;
}
}
uint8_t *DownloadBuffer::data(size_t offset) { uint8_t *DownloadBuffer::data(size_t offset) {
if (offset > this->size_) { if (offset > this->size_) {
ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!"); ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!");
@ -46,12 +55,13 @@ size_t DownloadBuffer::resize(size_t size) {
return size; return size;
} }
this->allocator_.deallocate(this->buffer_, this->size_); this->allocator_.deallocate(this->buffer_, this->size_);
this->size_ = size;
this->buffer_ = this->allocator_.allocate(size); this->buffer_ = this->allocator_.allocate(size);
this->reset(); this->reset();
if (this->buffer_) { if (this->buffer_) {
this->size_ = size;
return size; return size;
} else { } else {
this->size_ = 0;
return 0; return 0;
} }
} }

View File

@ -29,8 +29,12 @@ class ImageDecoder {
* @brief Initialize the decoder. * @brief Initialize the decoder.
* *
* @param download_size The total number of bytes that need to be downloaded for the image. * @param download_size The total number of bytes that need to be downloaded for the image.
* @return int Returns 0 on success, a {@see DecodeError} value in case of an error.
*/ */
virtual void prepare(size_t download_size) { this->download_size_ = download_size; } virtual int prepare(size_t download_size) {
this->download_size_ = download_size;
return 0;
}
/** /**
* @brief Decode a part of the image. It will try reading from the buffer. * @brief Decode a part of the image. It will try reading from the buffer.
@ -83,10 +87,7 @@ class ImageDecoder {
class DownloadBuffer { class DownloadBuffer {
public: public:
DownloadBuffer(size_t size) : size_(size) { DownloadBuffer(size_t size);
this->buffer_ = this->allocator_.allocate(size);
this->reset();
}
virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); } virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); }

View File

@ -41,13 +41,14 @@ static int draw_callback(JPEGDRAW *jpeg) {
return 1; return 1;
} }
void JpegDecoder::prepare(size_t download_size) { int JpegDecoder::prepare(size_t download_size) {
ImageDecoder::prepare(download_size); ImageDecoder::prepare(download_size);
auto size = this->image_->resize_download_buffer(download_size); auto size = this->image_->resize_download_buffer(download_size);
if (size < download_size) { if (size < download_size) {
ESP_LOGE(TAG, "Resize failed!"); ESP_LOGE(TAG, "Download buffer resize failed!");
// TODO: return an error code; return DECODE_ERROR_OUT_OF_MEMORY;
} }
return 0;
} }
int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) { int HOT JpegDecoder::decode(uint8_t *buffer, size_t size) {

View File

@ -21,7 +21,7 @@ class JpegDecoder : public ImageDecoder {
JpegDecoder(OnlineImage *image) : ImageDecoder(image) {} JpegDecoder(OnlineImage *image) : ImageDecoder(image) {}
~JpegDecoder() override {} ~JpegDecoder() override {}
void prepare(size_t download_size) override; int prepare(size_t download_size) override;
int HOT decode(uint8_t *buffer, size_t size) override; int HOT decode(uint8_t *buffer, size_t size) override;
protected: protected:

View File

@ -124,7 +124,7 @@ void OnlineImage::update() {
default: default:
accept_mime_type = "image/*"; accept_mime_type = "image/*";
} }
accept_header.value = (accept_mime_type + ",*/*;q=0.8").c_str(); accept_header.value = accept_mime_type + ",*/*;q=0.8";
headers.push_back(accept_header); headers.push_back(accept_header);
@ -178,7 +178,12 @@ void OnlineImage::update() {
this->download_error_callback_.call(); this->download_error_callback_.call();
return; return;
} }
this->decoder_->prepare(total_size); auto prepare_result = this->decoder_->prepare(total_size);
if (prepare_result < 0) {
this->end_connection_();
this->download_error_callback_.call();
return;
}
ESP_LOGI(TAG, "Downloading image (Size: %d)", total_size); ESP_LOGI(TAG, "Downloading image (Size: %d)", total_size);
this->start_time_ = ::time(nullptr); this->start_time_ = ::time(nullptr);
} }

View File

@ -40,11 +40,16 @@ static void draw_callback(pngle_t *pngle, uint32_t x, uint32_t y, uint32_t w, ui
decoder->draw(x, y, w, h, color); decoder->draw(x, y, w, h, color);
} }
void PngDecoder::prepare(size_t download_size) { int PngDecoder::prepare(size_t download_size) {
ImageDecoder::prepare(download_size); ImageDecoder::prepare(download_size);
if (!this->pngle_) {
ESP_LOGE(TAG, "PNG decoder engine not initialized!");
return DECODE_ERROR_OUT_OF_MEMORY;
}
pngle_set_user_data(this->pngle_, this); pngle_set_user_data(this->pngle_, this);
pngle_set_init_callback(this->pngle_, init_callback); pngle_set_init_callback(this->pngle_, init_callback);
pngle_set_draw_callback(this->pngle_, draw_callback); pngle_set_draw_callback(this->pngle_, draw_callback);
return 0;
} }
int HOT PngDecoder::decode(uint8_t *buffer, size_t size) { int HOT PngDecoder::decode(uint8_t *buffer, size_t size) {

View File

@ -21,7 +21,7 @@ class PngDecoder : public ImageDecoder {
PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {} PngDecoder(OnlineImage *image) : ImageDecoder(image), pngle_(pngle_new()) {}
~PngDecoder() override { pngle_destroy(this->pngle_); } ~PngDecoder() override { pngle_destroy(this->pngle_); }
void prepare(size_t download_size) override; int prepare(size_t download_size) override;
int HOT decode(uint8_t *buffer, size_t size) override; int HOT decode(uint8_t *buffer, size_t size) override;
protected: protected:

View File

@ -182,13 +182,21 @@ AudioPipelineState AudioPipeline::process_state() {
if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) { if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) {
// Stop command is fully processed, so clear the command bit // Stop command is fully processed, so clear the command bit
xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP); xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
this->hard_stop_ = true;
} }
if (!this->is_playing_) { if (!this->is_playing_) {
// The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks // The tasks have been stopped for two ``process_state`` calls in a row, so delete the tasks
if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) { if ((this->read_task_handle_ != nullptr) || (this->decode_task_handle_ != nullptr)) {
this->delete_tasks_(); this->delete_tasks_();
if (this->hard_stop_) {
// Stop command was sent, so immediately end of the playback
this->speaker_->stop(); this->speaker_->stop();
this->hard_stop_ = false;
} else {
// Decoded all the audio, so let the speaker finish playing before stopping
this->speaker_->finish();
}
} }
} }
this->is_playing_ = false; this->is_playing_ = false;

View File

@ -112,6 +112,7 @@ class AudioPipeline {
uint32_t playback_ms_{0}; uint32_t playback_ms_{0};
bool hard_stop_{false};
bool is_playing_{false}; bool is_playing_{false};
bool pause_state_{false}; bool pause_state_{false};
bool task_stack_in_psram_; bool task_stack_in_psram_;

View File

@ -1,4 +1,5 @@
#include "voice_assistant.h" #include "voice_assistant.h"
#include "esphome/core/defines.h"
#ifdef USE_VOICE_ASSISTANT #ifdef USE_VOICE_ASSISTANT
@ -127,7 +128,7 @@ void VoiceAssistant::clear_buffers_() {
} }
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_buffer_ != nullptr) { if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) {
memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE); memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE);
this->speaker_buffer_size_ = 0; this->speaker_buffer_size_ = 0;
@ -159,7 +160,7 @@ void VoiceAssistant::deallocate_buffers_() {
this->input_buffer_ = nullptr; this->input_buffer_ = nullptr;
#ifdef USE_SPEAKER #ifdef USE_SPEAKER
if (this->speaker_buffer_ != nullptr) { if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) {
ExternalRAMAllocator<uint8_t> speaker_deallocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE); ExternalRAMAllocator<uint8_t> speaker_deallocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
speaker_deallocator.deallocate(this->speaker_buffer_, SPEAKER_BUFFER_SIZE); speaker_deallocator.deallocate(this->speaker_buffer_, SPEAKER_BUFFER_SIZE);
this->speaker_buffer_ = nullptr; this->speaker_buffer_ = nullptr;
@ -389,14 +390,7 @@ void VoiceAssistant::loop() {
} }
#endif #endif
if (playing) { if (playing) {
this->set_timeout("playing", 2000, [this]() { this->start_playback_timeout_();
this->cancel_timeout("speaker-timeout");
this->set_state_(State::IDLE, State::IDLE);
api::VoiceAssistantAnnounceFinished msg;
msg.success = true;
this->api_client_->send_voice_assistant_announce_finished(msg);
});
} }
break; break;
} }
@ -614,6 +608,8 @@ void VoiceAssistant::request_stop() {
this->desired_state_ = State::IDLE; this->desired_state_ = State::IDLE;
break; break;
case State::AWAITING_RESPONSE: case State::AWAITING_RESPONSE:
this->signal_stop_();
break;
case State::STREAMING_RESPONSE: case State::STREAMING_RESPONSE:
case State::RESPONSE_FINISHED: case State::RESPONSE_FINISHED:
break; // Let the incoming audio stream finish then it will go to idle. break; // Let the incoming audio stream finish then it will go to idle.
@ -631,6 +627,17 @@ void VoiceAssistant::signal_stop_() {
this->api_client_->send_voice_assistant_request(msg); this->api_client_->send_voice_assistant_request(msg);
} }
void VoiceAssistant::start_playback_timeout_() {
this->set_timeout("playing", 100, [this]() {
this->cancel_timeout("speaker-timeout");
this->set_state_(State::IDLE, State::IDLE);
api::VoiceAssistantAnnounceFinished msg;
msg.success = true;
this->api_client_->send_voice_assistant_announce_finished(msg);
});
}
void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) { void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
ESP_LOGD(TAG, "Event Type: %" PRId32, msg.event_type); ESP_LOGD(TAG, "Event Type: %" PRId32, msg.event_type);
switch (msg.event_type) { switch (msg.event_type) {
@ -715,6 +722,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
#ifdef USE_MEDIA_PLAYER #ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) { if (this->media_player_ != nullptr) {
this->media_player_->make_call().set_media_url(url).set_announcement(true).perform(); this->media_player_->make_call().set_media_url(url).set_announcement(true).perform();
// Start the playback timeout, as the media player state isn't immediately updated
this->start_playback_timeout_();
} }
#endif #endif
this->tts_end_trigger_->trigger(url); this->tts_end_trigger_->trigger(url);
@ -725,7 +734,11 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
} }
case api::enums::VOICE_ASSISTANT_RUN_END: { case api::enums::VOICE_ASSISTANT_RUN_END: {
ESP_LOGD(TAG, "Assist Pipeline ended"); ESP_LOGD(TAG, "Assist Pipeline ended");
if (this->state_ == State::STREAMING_MICROPHONE) { if ((this->state_ == State::STARTING_PIPELINE) || (this->state_ == State::AWAITING_RESPONSE)) {
// Pipeline ended before starting microphone
// Or there wasn't a TTS start event ("nevermind")
this->set_state_(State::IDLE, State::IDLE);
} else if (this->state_ == State::STREAMING_MICROPHONE) {
this->ring_buffer_->reset(); this->ring_buffer_->reset();
#ifdef USE_ESP_ADF #ifdef USE_ESP_ADF
if (this->use_wake_word_) { if (this->use_wake_word_) {
@ -736,9 +749,6 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
{ {
this->set_state_(State::IDLE, State::IDLE); this->set_state_(State::IDLE, State::IDLE);
} }
} else if (this->state_ == State::AWAITING_RESPONSE) {
// No TTS start event ("nevermind")
this->set_state_(State::IDLE, State::IDLE);
} }
this->defer([this]() { this->end_trigger_->trigger(); }); this->defer([this]() { this->end_trigger_->trigger(); });
break; break;

View File

@ -40,6 +40,7 @@ enum VoiceAssistantFeature : uint32_t {
FEATURE_SPEAKER = 1 << 1, FEATURE_SPEAKER = 1 << 1,
FEATURE_API_AUDIO = 1 << 2, FEATURE_API_AUDIO = 1 << 2,
FEATURE_TIMERS = 1 << 3, FEATURE_TIMERS = 1 << 3,
FEATURE_ANNOUNCE = 1 << 4,
}; };
enum class State { enum class State {
@ -136,6 +137,12 @@ class VoiceAssistant : public Component {
flags |= VoiceAssistantFeature::FEATURE_TIMERS; flags |= VoiceAssistantFeature::FEATURE_TIMERS;
} }
#ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) {
flags |= VoiceAssistantFeature::FEATURE_ANNOUNCE;
}
#endif
return flags; return flags;
} }
@ -209,6 +216,7 @@ class VoiceAssistant : public Component {
void set_state_(State state); void set_state_(State state);
void set_state_(State state, State desired_state); void set_state_(State state, State desired_state);
void signal_stop_(); void signal_stop_();
void start_playback_timeout_();
std::unique_ptr<socket::Socket> socket_ = nullptr; std::unique_ptr<socket::Socket> socket_ = nullptr;
struct sockaddr_storage dest_addr_; struct sockaddr_storage dest_addr_;

View File

@ -55,6 +55,9 @@ GDEW029T5 = waveshare_epaper_ns.class_("GDEW029T5", WaveshareEPaper)
WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_( WaveshareEPaper2P9InDKE = waveshare_epaper_ns.class_(
"WaveshareEPaper2P9InDKE", WaveshareEPaper "WaveshareEPaper2P9InDKE", WaveshareEPaper
) )
WaveshareEPaper2P9InD = waveshare_epaper_ns.class_(
"WaveshareEPaper2P9InD", WaveshareEPaper
)
WaveshareEPaper4P2In = waveshare_epaper_ns.class_( WaveshareEPaper4P2In = waveshare_epaper_ns.class_(
"WaveshareEPaper4P2In", WaveshareEPaper "WaveshareEPaper4P2In", WaveshareEPaper
) )
@ -120,7 +123,7 @@ MODELS = {
"2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74), "2.13in-ttgo-b74": ("a", WaveshareEPaperTypeAModel.TTGO_EPAPER_2_13_IN_B74),
"2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN), "2.90in": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN),
"2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2), "2.90inv2": ("a", WaveshareEPaperTypeAModel.WAVESHARE_EPAPER_2_9_IN_V2),
"gdew029t5": ("b", GDEW029T5), "gdew029t5": ("c", GDEW029T5),
"2.70in": ("b", WaveshareEPaper2P7In), "2.70in": ("b", WaveshareEPaper2P7In),
"2.70in-b": ("b", WaveshareEPaper2P7InB), "2.70in-b": ("b", WaveshareEPaper2P7InB),
"2.70in-bv2": ("b", WaveshareEPaper2P7InBV2), "2.70in-bv2": ("b", WaveshareEPaper2P7InBV2),
@ -128,6 +131,7 @@ MODELS = {
"2.90in-b": ("b", WaveshareEPaper2P9InB), "2.90in-b": ("b", WaveshareEPaper2P9InB),
"2.90in-bv3": ("b", WaveshareEPaper2P9InBV3), "2.90in-bv3": ("b", WaveshareEPaper2P9InBV3),
"2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2), "2.90inv2-r2": ("c", WaveshareEPaper2P9InV2R2),
"2.90in-d": ("b", WaveshareEPaper2P9InD),
"2.90in-dke": ("c", WaveshareEPaper2P9InDKE), "2.90in-dke": ("c", WaveshareEPaper2P9InDKE),
"4.20in": ("b", WaveshareEPaper4P2In), "4.20in": ("b", WaveshareEPaper4P2In),
"4.20in-bv2": ("b", WaveshareEPaper4P2InBV2), "4.20in-bv2": ("b", WaveshareEPaper4P2InBV2),

View File

@ -1211,6 +1211,93 @@ void WaveshareEPaper2P9InB::dump_config() {
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
// ========================================================
// Waveshare 2.9-inch E-Paper (Type D)
// Waveshare WIKI: https://www.waveshare.com/wiki/Pico-ePaper-2.9-D
// Datasheet: https://www.waveshare.com/w/upload/b/b5/2.9inch_e-Paper_(D)_Specification.pdf
// ========================================================
void WaveshareEPaper2P9InD::initialize() {
// EPD hardware init start
this->reset_();
// Booster Soft Start
this->command(0x06); // Command: BTST
this->data(0x17); // Soft start configuration Phase A
this->data(0x17); // Soft start configuration Phase B
this->data(0x17); // Soft start configuration Phase C
// Power Setting
this->command(0x01); // Command: PWR
this->data(0x03); // Intern DC/DC for VDH/VDL and VGH/VGL
this->data(0x00); // Default configuration VCOM_HV and VGHL_LV
this->data(0x2b); // VDH = 10.8 V
this->data(0x2b); // VDL = -10.8 V
// Power ON
this->command(0x04); // Command: PON
this->wait_until_idle_();
// Panel settings
this->command(0x00); // Command: PSR
this->data(0x1F); // LUT from OTP, black and white mode, default scan
// PLL Control
this->command(0x30); // Command: PLL
this->data(0x3A); // Default PLL frequency
// Resolution settings
this->command(0x61); // Command: TRES
this->data(0x80); // Width: 128
this->data(0x01); // Height MSB: 296
this->data(0x28); // Height LSB: 296
// VCOM and data interval settings
this->command(0x50); // Command: CDI
this->data(0x77);
// VCOM_DC settings
this->command(0x82); // Command: VDCS
this->data(0x12); // Dafault VCOM_DC
}
void WaveshareEPaper2P9InD::display() {
// Start transmitting old data (clearing buffer)
this->command(0x10); // Command: DTM1 (OLD frame data)
this->start_data_();
this->write_array(this->buffer_, this->get_buffer_length_());
this->end_data_();
// Start transmitting new data (updated content)
this->command(0x13); // Command: DTM2 (NEW frame data)
this->start_data_();
this->write_array(this->buffer_, this->get_buffer_length_());
this->end_data_();
// Refresh Display
this->command(0x12); // Command: DRF
this->wait_until_idle_();
// Enter Power Off
this->command(0x02); // Command: POF
this->wait_until_idle_();
// Enter Deep Sleep
this->command(0x07); // Command: DSLP
this->data(0xA5);
}
int WaveshareEPaper2P9InD::get_width_internal() { return 128; }
int WaveshareEPaper2P9InD::get_height_internal() { return 296; }
void WaveshareEPaper2P9InD::dump_config() {
LOG_DISPLAY("", "Waveshare E-Paper", this);
ESP_LOGCONFIG(TAG, " Model: 2.9in (D)");
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_UPDATE_INTERVAL(this);
}
// DKE 2.9 // DKE 2.9
// https://www.badge.team/docs/badges/sha2017/hardware/#e-ink-display-the-dke-group-depg0290b1 // https://www.badge.team/docs/badges/sha2017/hardware/#e-ink-display-the-dke-group-depg0290b1
// https://www.badge.team/docs/badges/sha2017/hardware/DEPG0290B01V3.0.pdf // https://www.badge.team/docs/badges/sha2017/hardware/DEPG0290B01V3.0.pdf
@ -1596,15 +1683,108 @@ void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every)
// Datasheet: // Datasheet:
// - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf // - https://v4.cecdn.yun300.cn/100001_1909185148/SSD1680.pdf
// - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h // - https://github.com/adafruit/Adafruit_EPD/blob/master/src/panels/ThinkInk_290_Grayscale4_T5.h
// - https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp
// - http://www.e-paper-display.com/GDEW029T5%20V3.1%20Specification5c22.pdf?
// ======================================================== // ========================================================
void GDEW029T5::initialize() { // full screen update LUT
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37 static const uint8_t LUT_20_VCOMDC_29_5[] = {
// EPD hardware init start 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x60, 0x28, 0x28, 0x00, 0x00, 0x01, 0x00, 0x14, 0x00,
0x00, 0x00, 0x01, 0x00, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_21_WW_29_5[] = {
0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_22_BW_29_5[] = {
0x40, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x40, 0x14,
0x00, 0x00, 0x00, 0x01, 0xA0, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_23_WB_29_5[] = {
0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_24_BB_29_5[] = {
0x80, 0x08, 0x00, 0x00, 0x00, 0x02, 0x90, 0x28, 0x28, 0x00, 0x00, 0x01, 0x80, 0x14,
0x00, 0x00, 0x00, 0x01, 0x50, 0x12, 0x12, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// partial screen update LUT
static const uint8_t LUT_20_VCOMDC_PARTIAL_29_5[] = {
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_21_WW_PARTIAL_29_5[] = {
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_22_BW_PARTIAL_29_5[] = {
0x80, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_23_WB_PARTIAL_29_5[] = {
0x40, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static const uint8_t LUT_24_BB_PARTIAL_29_5[] = {
0x00, 0x20, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
void GDEW029T5::power_on_() {
if (!this->power_is_on_) {
this->command(0x04);
this->wait_until_idle_();
}
this->power_is_on_ = true;
}
void GDEW029T5::power_off_() {
this->command(0x02);
this->wait_until_idle_();
this->power_is_on_ = false;
}
void GDEW029T5::deep_sleep() {
this->power_off_();
if (this->deep_sleep_between_updates_) {
this->command(0x07); // deep sleep
this->data(0xA5); // check code
ESP_LOGD(TAG, "go to deep sleep");
this->is_deep_sleep_ = true;
}
}
void GDEW029T5::init_display_() {
// from https://github.com/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp
// Hardware Initialization
if (this->deep_sleep_between_updates_ && this->is_deep_sleep_) {
ESP_LOGI(TAG, "wake up from deep sleep");
this->reset_(); this->reset_();
this->is_deep_sleep_ = false;
}
// COMMAND POWER SETTINGS // COMMAND POWER SETTINGS
this->command(0x00); this->command(0x01);
this->data(0x03); this->data(0x03);
this->data(0x00); this->data(0x00);
this->data(0x2b); this->data(0x2b);
@ -1617,40 +1797,122 @@ void GDEW029T5::initialize() {
this->data(0x17); this->data(0x17);
this->data(0x17); this->data(0x17);
// COMMAND POWER ON this->power_on_();
this->command(0x04);
this->wait_until_idle_();
// Not sure what this does but it's in the Adafruit EPD library
this->command(0xFF);
this->wait_until_idle_();
// COMMAND PANEL SETTING // COMMAND PANEL SETTING
this->command(0x00); this->command(0x00);
// 128x296 resolution: 10 // 128x296 resolution: 10
// LUT from OTP: 0 // LUT from register: 1
// B/W mode (doesn't work): 1 // B/W mode (doesn't work): 1
// scan-up: 1 // scan-up: 1
// shift-right: 1 // shift-right: 1
// booster ON: 1 // booster ON: 1
// no soft reset: 1 // no soft reset: 1
this->data(0b10011111); this->data(0b10111111);
this->data(0x0d); // VCOM to 0V fast
this->command(0x30); // PLL setting
this->data(0x3a); // 3a 100HZ 29 150Hz 39 200HZ 31 171HZ
this->command(0x61); // resolution setting
this->data(this->get_width_internal());
this->data(this->get_height_internal() >> 8);
this->data(this->get_height_internal() & 0xFF);
// COMMAND RESOLUTION SETTING ESP_LOGD(TAG, "panel setting done");
// set to 128x296 by COMMAND PANEL SETTING
// COMMAND VCOM AND DATA INTERVAL SETTING
// use defaults for white border and ESPHome image polarity
// EPD hardware init end
} }
void GDEW029T5::initialize() {
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
if (this->reset_pin_ != nullptr)
this->deep_sleep_between_updates_ = true;
// old buffer for partial update
ExternalRAMAllocator<uint8_t> allocator(ExternalRAMAllocator<uint8_t>::ALLOW_FAILURE);
this->old_buffer_ = allocator.allocate(this->get_buffer_length_());
if (this->old_buffer_ == nullptr) {
ESP_LOGE(TAG, "Could not allocate old buffer for display!");
return;
}
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
this->old_buffer_[i] = 0xFF;
}
}
// initialize for full(normal) update
void GDEW029T5::init_full_() {
this->init_display_();
this->command(0x82); // vcom_DC setting
this->data(0x08);
this->command(0X50); // VCOM AND DATA INTERVAL SETTING
this->data(0x97); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
this->command(0x20);
this->write_lut_(LUT_20_VCOMDC_29_5, sizeof(LUT_20_VCOMDC_29_5));
this->command(0x21);
this->write_lut_(LUT_21_WW_29_5, sizeof(LUT_21_WW_29_5));
this->command(0x22);
this->write_lut_(LUT_22_BW_29_5, sizeof(LUT_22_BW_29_5));
this->command(0x23);
this->write_lut_(LUT_23_WB_29_5, sizeof(LUT_23_WB_29_5));
this->command(0x24);
this->write_lut_(LUT_24_BB_29_5, sizeof(LUT_24_BB_29_5));
ESP_LOGD(TAG, "initialized full update");
}
// initialzie for partial update
void GDEW029T5::init_partial_() {
this->init_display_();
this->command(0x82); // vcom_DC setting
this->data(0x08);
this->command(0X50); // VCOM AND DATA INTERVAL SETTING
this->data(0x17); // WBmode:VBDF 17|D7 VBDW 97 VBDB 57 WBRmode:VBDF F7 VBDW 77 VBDB 37 VBDR B7
this->command(0x20);
this->write_lut_(LUT_20_VCOMDC_PARTIAL_29_5, sizeof(LUT_20_VCOMDC_PARTIAL_29_5));
this->command(0x21);
this->write_lut_(LUT_21_WW_PARTIAL_29_5, sizeof(LUT_21_WW_PARTIAL_29_5));
this->command(0x22);
this->write_lut_(LUT_22_BW_PARTIAL_29_5, sizeof(LUT_22_BW_PARTIAL_29_5));
this->command(0x23);
this->write_lut_(LUT_23_WB_PARTIAL_29_5, sizeof(LUT_23_WB_PARTIAL_29_5));
this->command(0x24);
this->write_lut_(LUT_24_BB_PARTIAL_29_5, sizeof(LUT_24_BB_PARTIAL_29_5));
ESP_LOGD(TAG, "initialized partial update");
}
void HOT GDEW029T5::display() { void HOT GDEW029T5::display() {
bool full_update = this->at_update_ == 0;
if (full_update) {
this->init_full_();
} else {
this->init_partial_();
this->command(0x91); // partial in
// set partial window
this->command(0x90);
// this->data(0);
this->data(0);
// this->data(0);
this->data((this->get_width_internal() - 1) % 256);
this->data(0);
this->data(0);
this->data(((this->get_height_internal() - 1)) / 256);
this->data(((this->get_height_internal() - 1)) % 256);
this->data(0x01);
}
// input old buffer data
this->command(0x10);
delay(2);
this->start_data_();
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
this->write_byte(this->old_buffer_[i]);
}
this->end_data_();
delay(2);
// COMMAND DATA START TRANSMISSION 2 (B/W only) // COMMAND DATA START TRANSMISSION 2 (B/W only)
this->command(0x13); this->command(0x13);
delay(2); delay(2);
this->start_data_(); this->start_data_();
for (size_t i = 0; i < this->get_buffer_length_(); i++) { for (size_t i = 0; i < this->get_buffer_length_(); i++) {
this->write_byte(this->buffer_[i]); this->write_byte(this->buffer_[i]);
this->old_buffer_[i] = this->buffer_[i];
} }
this->end_data_(); this->end_data_();
delay(2); delay(2);
@ -1660,10 +1922,28 @@ void HOT GDEW029T5::display() {
delay(2); delay(2);
this->wait_until_idle_(); this->wait_until_idle_();
// COMMAND POWER OFF if (full_update) {
// NOTE: power off < deep sleep ESP_LOGD(TAG, "full update done");
this->command(0x02); } else {
this->command(0x92); // partial out
ESP_LOGD(TAG, "partial update done");
} }
this->at_update_ = (this->at_update_ + 1) % this->full_update_every_;
// COMMAND deep sleep
this->deep_sleep();
}
void GDEW029T5::write_lut_(const uint8_t *lut, const uint8_t size) {
// COMMAND WRITE LUT REGISTER
this->start_data_();
for (uint8_t i = 0; i < size; i++)
this->write_byte(lut[i]);
this->end_data_();
}
void GDEW029T5::set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
int GDEW029T5::get_width_internal() { return 128; } int GDEW029T5::get_width_internal() { return 128; }
int GDEW029T5::get_height_internal() { return 296; } int GDEW029T5::get_height_internal() { return 296; }
void GDEW029T5::dump_config() { void GDEW029T5::dump_config() {
@ -1672,6 +1952,7 @@ void GDEW029T5::dump_config() {
LOG_PIN(" Reset Pin: ", this->reset_pin_); LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_); LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_); LOG_PIN(" Busy Pin: ", this->busy_pin_);
ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }

View File

@ -254,16 +254,27 @@ class GDEW029T5 : public WaveshareEPaper {
void dump_config() override; void dump_config() override;
void deep_sleep() override { void deep_sleep() override;
// COMMAND DEEP SLEEP void set_full_update_every(uint32_t full_update_every);
this->command(0x07);
this->data(0xA5); // check byte
}
protected: protected:
void init_display_();
void init_full_();
void init_partial_();
void write_lut_(const uint8_t *lut, uint8_t size);
void power_off_();
void power_on_();
int get_width_internal() override; int get_width_internal() override;
int get_height_internal() override; int get_height_internal() override;
private:
uint32_t full_update_every_{30};
uint32_t at_update_{0};
bool deep_sleep_between_updates_{false};
bool power_is_on_{false};
bool is_deep_sleep_{false};
uint8_t *old_buffer_{nullptr};
}; };
class WaveshareEPaper2P7InV2 : public WaveshareEPaper { class WaveshareEPaper2P7InV2 : public WaveshareEPaper {
@ -416,6 +427,26 @@ class WaveshareEPaper2P9InDKE : public WaveshareEPaper {
int get_height_internal() override; int get_height_internal() override;
}; };
class WaveshareEPaper2P9InD : public WaveshareEPaper {
public:
void initialize() override;
void display() override;
void dump_config() override;
void deep_sleep() override {
// COMMAND DEEP SLEEP
this->command(0x07);
this->data(0xA5);
}
protected:
int get_width_internal() override;
int get_height_internal() override;
};
class WaveshareEPaper4P2In : public WaveshareEPaper { class WaveshareEPaper4P2In : public WaveshareEPaper {
public: public:
void initialize() override; void initialize() override;

View File

@ -459,6 +459,7 @@ display:
reset_pin: reset_pin:
allow_other_uses: true allow_other_uses: true
number: ${reset_pin} number: ${reset_pin}
full_update_every: 30
lambda: |- lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height()); it.rectangle(0, 0, it.get_width(), it.get_height());
@ -713,6 +714,25 @@ display:
lambda: |- lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height()); it.rectangle(0, 0, it.get_width(), it.get_height());
- platform: waveshare_epaper
model: 2.90in-d
spi_id: spi_waveshare_epaper
cs_pin:
allow_other_uses: true
number: ${cs_pin}
dc_pin:
allow_other_uses: true
number: ${dc_pin}
busy_pin:
allow_other_uses: true
number: ${busy_pin}
reset_pin:
allow_other_uses: true
number: ${reset_pin}
reset_duration: 200ms
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
- platform: waveshare_epaper - platform: waveshare_epaper
model: 2.90in model: 2.90in
spi_id: spi_waveshare_epaper spi_id: spi_waveshare_epaper