1
0
mirror of https://github.com/esphome/esphome.git synced 2025-03-14 22:58:13 +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 {
struct Header {
const char *name;
const char *value;
std::string name;
std::string value;
};
// 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_);
}
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

View File

@ -84,7 +84,7 @@ std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::strin
container->set_secure(secure);
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();

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) {
if (offset > this->size_) {
ESP_LOGE(TAG, "Tried to access beyond download buffer bounds!!!");
@ -46,12 +55,13 @@ size_t DownloadBuffer::resize(size_t size) {
return size;
}
this->allocator_.deallocate(this->buffer_, this->size_);
this->size_ = size;
this->buffer_ = this->allocator_.allocate(size);
this->reset();
if (this->buffer_) {
this->size_ = size;
return size;
} else {
this->size_ = 0;
return 0;
}
}

View File

@ -29,8 +29,12 @@ class ImageDecoder {
* @brief Initialize the decoder.
*
* @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.
@ -83,10 +87,7 @@ class ImageDecoder {
class DownloadBuffer {
public:
DownloadBuffer(size_t size) : size_(size) {
this->buffer_ = this->allocator_.allocate(size);
this->reset();
}
DownloadBuffer(size_t size);
virtual ~DownloadBuffer() { this->allocator_.deallocate(this->buffer_, this->size_); }

View File

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

View File

@ -124,7 +124,7 @@ void OnlineImage::update() {
default:
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);
@ -178,7 +178,12 @@ void OnlineImage::update() {
this->download_error_callback_.call();
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);
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);
}
void PngDecoder::prepare(size_t download_size) {
int PngDecoder::prepare(size_t 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_init_callback(this->pngle_, init_callback);
pngle_set_draw_callback(this->pngle_, draw_callback);
return 0;
}
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() 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;
protected:

View File

@ -182,13 +182,21 @@ AudioPipelineState AudioPipeline::process_state() {
if (event_bits & EventGroupBits::PIPELINE_COMMAND_STOP) {
// Stop command is fully processed, so clear the command bit
xEventGroupClearBits(this->event_group_, EventGroupBits::PIPELINE_COMMAND_STOP);
this->hard_stop_ = true;
}
if (!this->is_playing_) {
// 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)) {
this->delete_tasks_();
this->speaker_->stop();
if (this->hard_stop_) {
// Stop command was sent, so immediately end of the playback
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;

View File

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

View File

@ -1,4 +1,5 @@
#include "voice_assistant.h"
#include "esphome/core/defines.h"
#ifdef USE_VOICE_ASSISTANT
@ -127,7 +128,7 @@ void VoiceAssistant::clear_buffers_() {
}
#ifdef USE_SPEAKER
if (this->speaker_buffer_ != nullptr) {
if ((this->speaker_ != nullptr) && (this->speaker_buffer_ != nullptr)) {
memset(this->speaker_buffer_, 0, SPEAKER_BUFFER_SIZE);
this->speaker_buffer_size_ = 0;
@ -159,7 +160,7 @@ void VoiceAssistant::deallocate_buffers_() {
this->input_buffer_ = nullptr;
#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);
speaker_deallocator.deallocate(this->speaker_buffer_, SPEAKER_BUFFER_SIZE);
this->speaker_buffer_ = nullptr;
@ -389,14 +390,7 @@ void VoiceAssistant::loop() {
}
#endif
if (playing) {
this->set_timeout("playing", 2000, [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);
});
this->start_playback_timeout_();
}
break;
}
@ -614,6 +608,8 @@ void VoiceAssistant::request_stop() {
this->desired_state_ = State::IDLE;
break;
case State::AWAITING_RESPONSE:
this->signal_stop_();
break;
case State::STREAMING_RESPONSE:
case State::RESPONSE_FINISHED:
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);
}
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) {
ESP_LOGD(TAG, "Event Type: %" PRId32, msg.event_type);
switch (msg.event_type) {
@ -715,6 +722,8 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
#ifdef USE_MEDIA_PLAYER
if (this->media_player_ != nullptr) {
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
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: {
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();
#ifdef USE_ESP_ADF
if (this->use_wake_word_) {
@ -736,9 +749,6 @@ void VoiceAssistant::on_event(const api::VoiceAssistantEventResponse &msg) {
{
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(); });
break;

View File

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

View File

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

View File

@ -1211,6 +1211,93 @@ void WaveshareEPaper2P9InB::dump_config() {
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
// 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
@ -1596,15 +1683,108 @@ void WaveshareEPaper2P9InV2R2::set_full_update_every(uint32_t full_update_every)
// Datasheet:
// - 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/ZinggJM/GxEPD2/blob/master/src/epd/GxEPD2_290_T5.cpp
// - http://www.e-paper-display.com/GDEW029T5%20V3.1%20Specification5c22.pdf?
// ========================================================
void GDEW029T5::initialize() {
// from https://www.waveshare.com/w/upload/b/bb/2.9inch-e-paper-b-specification.pdf, page 37
// EPD hardware init start
this->reset_();
// full screen update LUT
static const uint8_t LUT_20_VCOMDC_29_5[] = {
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->is_deep_sleep_ = false;
}
// COMMAND POWER SETTINGS
this->command(0x00);
this->command(0x01);
this->data(0x03);
this->data(0x00);
this->data(0x2b);
@ -1617,40 +1797,122 @@ void GDEW029T5::initialize() {
this->data(0x17);
this->data(0x17);
// COMMAND 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_();
this->power_on_();
// COMMAND PANEL SETTING
this->command(0x00);
// 128x296 resolution: 10
// LUT from OTP: 0
// LUT from register: 1
// B/W mode (doesn't work): 1
// scan-up: 1
// shift-right: 1
// booster ON: 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
// 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
ESP_LOGD(TAG, "panel setting done");
}
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() {
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)
this->command(0x13);
delay(2);
this->start_data_();
for (size_t i = 0; i < this->get_buffer_length_(); i++) {
this->write_byte(this->buffer_[i]);
this->old_buffer_[i] = this->buffer_[i];
}
this->end_data_();
delay(2);
@ -1660,10 +1922,28 @@ void HOT GDEW029T5::display() {
delay(2);
this->wait_until_idle_();
// COMMAND POWER OFF
// NOTE: power off < deep sleep
this->command(0x02);
if (full_update) {
ESP_LOGD(TAG, "full update done");
} 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_height_internal() { return 296; }
void GDEW029T5::dump_config() {
@ -1672,6 +1952,7 @@ void GDEW029T5::dump_config() {
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" DC Pin: ", this->dc_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
ESP_LOGCONFIG(TAG, " Full Update Every: %" PRIu32, this->full_update_every_);
LOG_UPDATE_INTERVAL(this);
}

View File

@ -254,16 +254,27 @@ class GDEW029T5 : public WaveshareEPaper {
void dump_config() override;
void deep_sleep() override {
// COMMAND DEEP SLEEP
this->command(0x07);
this->data(0xA5); // check byte
}
void deep_sleep() override;
void set_full_update_every(uint32_t full_update_every);
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_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 {
@ -416,6 +427,26 @@ class WaveshareEPaper2P9InDKE : public WaveshareEPaper {
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 {
public:
void initialize() override;

View File

@ -459,6 +459,7 @@ display:
reset_pin:
allow_other_uses: true
number: ${reset_pin}
full_update_every: 30
lambda: |-
it.rectangle(0, 0, it.get_width(), it.get_height());
@ -713,6 +714,25 @@ display:
lambda: |-
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
model: 2.90in
spi_id: spi_waveshare_epaper