mirror of
https://github.com/esphome/esphome.git
synced 2025-09-01 10:52:19 +01:00
Split up source files
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
#include "epaper_spi.h"
|
||||
#include <bitset>
|
||||
#include <cinttypes>
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -9,21 +8,32 @@ namespace esphome::epaper_spi {
|
||||
|
||||
static const char *const TAG = "epaper_spi";
|
||||
|
||||
static const uint8_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to allow the loop to run
|
||||
|
||||
void EPaperBase::setup() {
|
||||
this->init_internal_(this->get_buffer_length());
|
||||
if (!this->init_buffer_(this->get_buffer_length())) {
|
||||
this->mark_failed("Failed to initialize buffer");
|
||||
return;
|
||||
}
|
||||
this->setup_pins_();
|
||||
this->spi_setup();
|
||||
}
|
||||
|
||||
bool EPaperBase::init_buffer_(size_t buffer_length) {
|
||||
if (!this->buffer_.init(buffer_length)) {
|
||||
return false;
|
||||
}
|
||||
this->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EPaperBase::setup_pins_() {
|
||||
this->dc_pin_->setup(); // OUTPUT
|
||||
this->dc_pin_->digital_write(false);
|
||||
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->setup(); // OUTPUT
|
||||
this->reset_pin_->digital_write(true);
|
||||
}
|
||||
|
||||
if (this->busy_pin_ != nullptr) {
|
||||
this->busy_pin_->setup(); // INPUT
|
||||
}
|
||||
@@ -44,13 +54,23 @@ void EPaperBase::data(uint8_t value) {
|
||||
}
|
||||
|
||||
// write a command followed by one or more bytes of data.
|
||||
// The command is the first byte, length is the total including cmd.
|
||||
void EPaperBase::cmd_data(const uint8_t *c_data, size_t length) {
|
||||
// The command is the first byte, length is the length of data only in the second byte, followed by the data.
|
||||
// [COMMAND, LENGTH, DATA...]
|
||||
void EPaperBase::cmd_data(const uint8_t *data) {
|
||||
const uint8_t command = data[0];
|
||||
const uint8_t length = data[1];
|
||||
const uint8_t *ptr = data + 2;
|
||||
|
||||
ESP_LOGVV(TAG, "Command: 0x%02X, Length: %d, Data: %s", command, length,
|
||||
format_hex_pretty(ptr, length, '.', false).c_str());
|
||||
|
||||
this->dc_pin_->digital_write(false);
|
||||
this->enable();
|
||||
this->write_byte(c_data[0]);
|
||||
this->dc_pin_->digital_write(true);
|
||||
this->write_array(c_data + 1, length - 1);
|
||||
this->write_byte(command);
|
||||
if (length > 0) {
|
||||
this->dc_pin_->digital_write(true);
|
||||
this->write_array(ptr, length);
|
||||
}
|
||||
this->disable();
|
||||
}
|
||||
|
||||
@@ -61,179 +81,79 @@ bool EPaperBase::is_idle_() {
|
||||
return !this->busy_pin_->digital_read();
|
||||
}
|
||||
|
||||
void EPaperBase::reset_() {
|
||||
void EPaperBase::reset() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->reset_pin_->digital_write(false);
|
||||
this->disable_loop();
|
||||
this->set_timeout(this->reset_duration_, [this] {
|
||||
this->reset_pin_->digital_write(true);
|
||||
this->set_timeout(20, [this] {
|
||||
this->state_ = RESET_DONE;
|
||||
this->enable_loop();
|
||||
});
|
||||
this->set_timeout(20, [this] { this->enable_loop(); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void EPaperBase::update() {
|
||||
if (this->state_ != IDLE) {
|
||||
if (!this->state_queue_.empty()) {
|
||||
ESP_LOGE(TAG, "Display update already in progress");
|
||||
return;
|
||||
}
|
||||
this->do_update_(); // Calls ESPHome (current page) lambda
|
||||
this->state_ = UPDATING;
|
||||
|
||||
this->state_queue_.push(EPaperState::UPDATE);
|
||||
this->state_queue_.push(EPaperState::RESET);
|
||||
this->state_queue_.push(EPaperState::INITIALISE);
|
||||
this->state_queue_.push(EPaperState::TRANSFER_DATA);
|
||||
this->state_queue_.push(EPaperState::POWER_ON);
|
||||
this->state_queue_.push(EPaperState::REFRESH_SCREEN);
|
||||
this->state_queue_.push(EPaperState::POWER_OFF);
|
||||
this->state_queue_.push(EPaperState::DEEP_SLEEP);
|
||||
this->state_queue_.push(EPaperState::IDLE);
|
||||
|
||||
this->enable_loop();
|
||||
}
|
||||
|
||||
void EPaperBase::loop() {
|
||||
switch (this->state_) {
|
||||
case IDLE:
|
||||
if (this->waiting_for_idle_) {
|
||||
if (this->is_idle_()) {
|
||||
this->waiting_for_idle_ = false;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto state = this->state_queue_.front();
|
||||
|
||||
switch (state) {
|
||||
case EPaperState::IDLE:
|
||||
this->disable_loop();
|
||||
break;
|
||||
case UPDATING:
|
||||
this->reset_();
|
||||
this->state_ = RESETTING;
|
||||
case EPaperState::UPDATE:
|
||||
this->do_update_(); // Calls ESPHome (current page) lambda
|
||||
break;
|
||||
case RESETTING:
|
||||
// Nothing to do here, next state change is handled by timeouts
|
||||
case EPaperState::RESET:
|
||||
this->reset();
|
||||
break;
|
||||
case RESET_DONE:
|
||||
if (this->is_idle_()) {
|
||||
this->initialize();
|
||||
this->state_ = INITIALIZING;
|
||||
case EPaperState::INITIALISE:
|
||||
this->initialize_();
|
||||
break;
|
||||
case EPaperState::TRANSFER_DATA:
|
||||
if (!this->transfer_data()) {
|
||||
return; // Not done yet, come back next loop
|
||||
}
|
||||
break;
|
||||
case INITIALIZING:
|
||||
if (this->is_idle_()) {
|
||||
ESP_LOGI(TAG, "Display initialized successfully");
|
||||
this->state_ = TRANSFERING_DATA;
|
||||
}
|
||||
break;
|
||||
case TRANSFERING_DATA:
|
||||
this->transfer_data();
|
||||
break;
|
||||
case TRANSFER_DONE:
|
||||
case EPaperState::POWER_ON:
|
||||
this->power_on();
|
||||
this->state_ = POWERING_ON;
|
||||
break;
|
||||
case POWERING_ON:
|
||||
if (this->is_idle_()) {
|
||||
this->refresh_screen();
|
||||
this->state_ = REFRESHING_SCREEN;
|
||||
}
|
||||
case EPaperState::REFRESH_SCREEN:
|
||||
this->refresh_screen();
|
||||
break;
|
||||
case REFRESHING_SCREEN:
|
||||
if (this->is_idle_()) {
|
||||
this->power_off();
|
||||
this->state_ = POWERING_OFF;
|
||||
}
|
||||
case EPaperState::POWER_OFF:
|
||||
this->power_off();
|
||||
break;
|
||||
case POWERING_OFF:
|
||||
if (this->is_idle_()) {
|
||||
this->deep_sleep();
|
||||
}
|
||||
this->state_ = IDLE;
|
||||
this->disable_loop();
|
||||
case EPaperState::DEEP_SLEEP:
|
||||
this->deep_sleep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EPaper6Color::setup() {
|
||||
if (!this->init_internal_6c_(this->get_buffer_length())) {
|
||||
this->mark_failed("Failed to initialize buffer");
|
||||
return;
|
||||
}
|
||||
this->setup_pins_();
|
||||
this->spi_setup();
|
||||
}
|
||||
|
||||
bool EPaper6Color::init_internal_6c_(uint32_t buffer_length) {
|
||||
if (!this->buffer_.init(buffer_length)) {
|
||||
return false;
|
||||
}
|
||||
this->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t EPaper6Color::color_to_hex(Color color) {
|
||||
uint8_t hex_code;
|
||||
if (color.red > 127) {
|
||||
if (color.green > 170) {
|
||||
if (color.blue > 127) {
|
||||
hex_code = 0x1; // White
|
||||
} else {
|
||||
hex_code = 0x2; // Yellow
|
||||
}
|
||||
} else {
|
||||
hex_code = 0x3; // Red (or Magenta)
|
||||
}
|
||||
} else {
|
||||
if (color.green > 127) {
|
||||
if (color.blue > 127) {
|
||||
hex_code = 0x5; // Cyan -> Blue
|
||||
} else {
|
||||
hex_code = 0x6; // Green
|
||||
}
|
||||
} else {
|
||||
if (color.blue > 127) {
|
||||
hex_code = 0x5; // Blue
|
||||
} else {
|
||||
hex_code = 0x0; // Black
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hex_code;
|
||||
}
|
||||
void EPaper6Color::fill(Color color) {
|
||||
uint8_t pixel_color;
|
||||
if (color.is_on()) {
|
||||
pixel_color = this->color_to_hex(color);
|
||||
} else {
|
||||
pixel_color = 0x1;
|
||||
}
|
||||
|
||||
// We store 8 bitset<3> in 3 bytes
|
||||
// | byte 1 | byte 2 | byte 3 |
|
||||
// |aaabbbaa|abbbaaab|bbaaabbb|
|
||||
uint8_t byte_1 = pixel_color << 5 | pixel_color << 2 | pixel_color >> 1;
|
||||
uint8_t byte_2 = pixel_color << 7 | pixel_color << 4 | pixel_color << 1 | pixel_color >> 2;
|
||||
uint8_t byte_3 = pixel_color << 6 | pixel_color << 3 | pixel_color << 0;
|
||||
|
||||
const size_t buffer_length = this->get_buffer_length();
|
||||
for (size_t i = 0; i < buffer_length; i += 3) {
|
||||
this->buffer_[i + 0] = byte_1;
|
||||
this->buffer_[i + 1] = byte_2;
|
||||
this->buffer_[i + 2] = byte_3;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EPaper6Color::get_buffer_length() {
|
||||
// 6 colors buffer, 1 pixel = 3 bits, we will store 8 pixels in 24 bits = 3 bytes
|
||||
return this->get_width_controller() * this->get_height_internal() / 8u * 3u;
|
||||
}
|
||||
|
||||
void HOT EPaper6Color::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
|
||||
return;
|
||||
|
||||
uint8_t pixel_bits = this->color_to_hex(color);
|
||||
uint32_t pixel_position = x + y * this->get_width_controller();
|
||||
uint32_t first_bit_position = pixel_position * 3;
|
||||
uint32_t byte_position = first_bit_position / 8u;
|
||||
uint32_t byte_subposition = first_bit_position % 8u;
|
||||
|
||||
if (byte_subposition <= 5) {
|
||||
this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 << (5 - byte_subposition)))) |
|
||||
(pixel_bits << (5 - byte_subposition));
|
||||
} else {
|
||||
this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 >> (byte_subposition - 5)))) |
|
||||
(pixel_bits >> (byte_subposition - 5));
|
||||
|
||||
this->buffer_[byte_position + 1] =
|
||||
(this->buffer_[byte_position + 1] & (0xFF ^ (0xFF & (0b111 << (13 - byte_subposition))))) |
|
||||
(pixel_bits << (13 - byte_subposition));
|
||||
}
|
||||
this->state_queue_.pop();
|
||||
}
|
||||
|
||||
void EPaperBase::start_command_() {
|
||||
@@ -251,112 +171,27 @@ void EPaperBase::end_data_() { this->disable(); }
|
||||
|
||||
void EPaperBase::on_safe_shutdown() { this->deep_sleep(); }
|
||||
|
||||
void EPaper7p3InE::initialize() {
|
||||
static const uint8_t cmdh_data[] = {0xAA, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18};
|
||||
this->cmd_data(cmdh_data, sizeof(cmdh_data));
|
||||
void EPaperBase::initialize_() {
|
||||
size_t index = 0;
|
||||
const auto &sequence = this->init_sequence_;
|
||||
const size_t sequence_size = this->init_sequence_length_;
|
||||
while (index != sequence_size) {
|
||||
if (sequence_size - index < 2) {
|
||||
this->mark_failed("Malformed init sequence");
|
||||
return;
|
||||
}
|
||||
const auto *ptr = sequence + index;
|
||||
const uint8_t length = ptr[1];
|
||||
if (sequence_size - index < length) {
|
||||
this->mark_failed("Malformed init sequence");
|
||||
return;
|
||||
}
|
||||
|
||||
static const uint8_t data_01[] = {0x01, 0x3F};
|
||||
this->cmd_data(data_01, sizeof(data_01));
|
||||
|
||||
static const uint8_t data_02[] = {0x00, 0x5F, 0x69};
|
||||
this->cmd_data(data_02, sizeof(data_02));
|
||||
|
||||
static const uint8_t data_03[] = {0x03, 0x00, 0x54, 0x00, 0x44};
|
||||
this->cmd_data(data_03, sizeof(data_03));
|
||||
|
||||
static const uint8_t data_04[] = {0x05, 0x40, 0x1F, 0x1F, 0x2C};
|
||||
this->cmd_data(data_04, sizeof(data_04));
|
||||
|
||||
static const uint8_t data_05[] = {0x06, 0x6F, 0x1F, 0x17, 0x49};
|
||||
this->cmd_data(data_05, sizeof(data_05));
|
||||
|
||||
static const uint8_t data_06[] = {0x08, 0x6F, 0x1F, 0x1F, 0x22};
|
||||
this->cmd_data(data_06, sizeof(data_06));
|
||||
|
||||
static const uint8_t data_07[] = {0x30, 0x03};
|
||||
this->cmd_data(data_07, sizeof(data_07));
|
||||
|
||||
static const uint8_t data_08[] = {0x50, 0x3F};
|
||||
this->cmd_data(data_08, sizeof(data_08));
|
||||
|
||||
static const uint8_t data_09[] = {0x60, 0x02, 0x00};
|
||||
this->cmd_data(data_09, sizeof(data_09));
|
||||
|
||||
static const uint8_t data_10[] = {0x61, 0x03, 0x20, 0x01, 0xE0};
|
||||
this->cmd_data(data_10, sizeof(data_10));
|
||||
|
||||
static const uint8_t data_11[] = {0x84, 0x01};
|
||||
this->cmd_data(data_11, sizeof(data_11));
|
||||
|
||||
static const uint8_t data_12[] = {0xE3, 0x2F};
|
||||
this->cmd_data(data_12, sizeof(data_12));
|
||||
this->cmd_data(ptr);
|
||||
index += length + 2;
|
||||
}
|
||||
|
||||
this->power_on();
|
||||
}
|
||||
|
||||
void HOT EPaper7p3InE::transfer_data() {
|
||||
const uint32_t start_time = App.get_loop_component_start_time();
|
||||
if (this->current_data_index_ == 0) {
|
||||
ESP_LOGI(TAG, "Sending data to the display");
|
||||
this->command(0x10);
|
||||
}
|
||||
|
||||
uint8_t bytes_to_send[4]{0};
|
||||
const size_t buffer_length = this->get_buffer_length();
|
||||
for (size_t i = this->current_data_index_; i < buffer_length; i += 3) {
|
||||
std::bitset<24> triplet = encode_uint24(this->buffer_[i + 0], this->buffer_[i + 1], this->buffer_[i + 2]);
|
||||
// 8 bitset<3> are stored in 3 bytes
|
||||
// |aaabbbaa|abbbaaab|bbaaabbb|
|
||||
// | byte 1 | byte 2 | byte 3 |
|
||||
bytes_to_send[0] = ((triplet >> 17).to_ulong() & 0b01110000) | ((triplet >> 18).to_ulong() & 0b00000111);
|
||||
bytes_to_send[1] = ((triplet >> 11).to_ulong() & 0b01110000) | ((triplet >> 12).to_ulong() & 0b00000111);
|
||||
bytes_to_send[2] = ((triplet >> 5).to_ulong() & 0b01110000) | ((triplet >> 6).to_ulong() & 0b00000111);
|
||||
bytes_to_send[3] = ((triplet << 1).to_ulong() & 0b01110000) | ((triplet << 0).to_ulong() & 0b00000111);
|
||||
|
||||
this->start_data_();
|
||||
this->write_array(bytes_to_send, sizeof(bytes_to_send));
|
||||
this->end_data_();
|
||||
|
||||
if (millis() - start_time > MAX_TRANSFER_TIME) {
|
||||
// Let the main loop run and come back next loop
|
||||
this->current_data_index_ = i + 3;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Finished the entire dataset
|
||||
this->current_data_index_ = 0;
|
||||
this->state_ = TRANSFER_DONE;
|
||||
}
|
||||
|
||||
void EPaper7p3InE::power_on() {
|
||||
ESP_LOGI(TAG, "Power on the display");
|
||||
this->command(0x04);
|
||||
}
|
||||
|
||||
void EPaper7p3InE::power_off() {
|
||||
ESP_LOGI(TAG, "Power off the display");
|
||||
this->command(0x02);
|
||||
this->data(0x00);
|
||||
}
|
||||
|
||||
void EPaper7p3InE::refresh_screen() {
|
||||
this->command(0x12);
|
||||
this->data(0x00);
|
||||
}
|
||||
|
||||
void EPaper7p3InE::deep_sleep() {
|
||||
ESP_LOGI(TAG, "Set the display to deep sleep");
|
||||
this->command(0x07);
|
||||
this->data(0xA5);
|
||||
}
|
||||
|
||||
void EPaper7p3InE::dump_config() {
|
||||
LOG_DISPLAY("", "E-Paper SPI", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 7.3in-E");
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace esphome::epaper_spi
|
||||
|
@@ -5,25 +5,30 @@
|
||||
#include "esphome/components/split_buffer/split_buffer.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <queue>
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
enum EPaperState : uint8_t {
|
||||
enum class EPaperState : uint8_t {
|
||||
IDLE,
|
||||
UPDATING,
|
||||
RESETTING,
|
||||
RESET_DONE,
|
||||
INITIALIZING,
|
||||
TRANSFERING_DATA,
|
||||
TRANSFER_DONE,
|
||||
POWERING_ON,
|
||||
REFRESHING_SCREEN,
|
||||
POWERING_OFF,
|
||||
UPDATE,
|
||||
RESET,
|
||||
INITIALISE,
|
||||
TRANSFER_DATA,
|
||||
POWER_ON,
|
||||
REFRESH_SCREEN,
|
||||
POWER_OFF,
|
||||
DEEP_SLEEP,
|
||||
};
|
||||
|
||||
static const uint8_t MAX_TRANSFER_TIME = 10; // Transfer in 10ms blocks to allow the loop to run
|
||||
|
||||
class EPaperBase : public display::DisplayBuffer,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||
spi::DATA_RATE_2MHZ> {
|
||||
public:
|
||||
EPaperBase(const uint8_t *init_sequence, const size_t init_sequence_length)
|
||||
: init_sequence_(init_sequence), init_sequence_length_(init_sequence_length) {}
|
||||
void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
|
||||
float get_setup_priority() const override;
|
||||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
@@ -32,7 +37,7 @@ class EPaperBase : public display::DisplayBuffer,
|
||||
|
||||
void command(uint8_t value);
|
||||
void data(uint8_t value);
|
||||
void cmd_data(const uint8_t *data, size_t length);
|
||||
void cmd_data(const uint8_t *data);
|
||||
|
||||
void update() override;
|
||||
void loop() override;
|
||||
@@ -44,12 +49,17 @@ class EPaperBase : public display::DisplayBuffer,
|
||||
protected:
|
||||
bool is_idle_();
|
||||
void setup_pins_();
|
||||
void reset_();
|
||||
virtual void reset();
|
||||
void initialize_();
|
||||
bool init_buffer_(size_t buffer_length);
|
||||
|
||||
virtual int get_width_controller() { return this->get_width_internal(); };
|
||||
virtual void initialize() = 0;
|
||||
virtual void deep_sleep() = 0;
|
||||
virtual void transfer_data() = 0;
|
||||
/**
|
||||
* Send data to the device via SPI
|
||||
* @return true if done, false if should be called next loop
|
||||
*/
|
||||
virtual bool transfer_data() = 0;
|
||||
virtual void refresh_screen() = 0;
|
||||
|
||||
virtual void power_on() = 0;
|
||||
@@ -65,45 +75,17 @@ class EPaperBase : public display::DisplayBuffer,
|
||||
GPIOPin *busy_pin_{nullptr};
|
||||
GPIOPin *reset_pin_{nullptr};
|
||||
|
||||
const uint8_t *init_sequence_{nullptr};
|
||||
const size_t init_sequence_length_{0};
|
||||
|
||||
uint32_t reset_duration_{200};
|
||||
|
||||
EPaperState state_{IDLE};
|
||||
|
||||
split_buffer::SplitBuffer buffer_;
|
||||
};
|
||||
|
||||
class EPaper6Color : public EPaperBase {
|
||||
public:
|
||||
uint8_t color_to_hex(Color color);
|
||||
void fill(Color color) override;
|
||||
|
||||
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
||||
|
||||
protected:
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
uint32_t get_buffer_length() override;
|
||||
void setup() override;
|
||||
bool init_internal_6c_(uint32_t buffer_length);
|
||||
};
|
||||
|
||||
class EPaper7p3InE : public EPaper6Color {
|
||||
public:
|
||||
void initialize() override;
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
int get_width_internal() override { return 800; };
|
||||
int get_height_internal() override { return 480; };
|
||||
void transfer_data() override;
|
||||
void refresh_screen() override;
|
||||
|
||||
void power_on() override;
|
||||
void power_off() override;
|
||||
|
||||
void deep_sleep() override;
|
||||
|
||||
size_t current_data_index_{0};
|
||||
|
||||
std::queue<EPaperState> state_queue_{{EPaperState::IDLE}};
|
||||
|
||||
bool waiting_for_idle_{false};
|
||||
};
|
||||
|
||||
} // namespace esphome::epaper_spi
|
||||
|
135
esphome/components/epaper_spi/epaper_spi_base_6_color.cpp
Normal file
135
esphome/components/epaper_spi/epaper_spi_base_6_color.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
#include "epaper_spi_base_6_color.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
static constexpr const char *const TAG = "epaper_spi.6c";
|
||||
|
||||
static inline uint8_t color_to_hex(Color color) {
|
||||
if (color.red > 127) {
|
||||
if (color.green > 170) {
|
||||
if (color.blue > 127) {
|
||||
return 0x1; // White
|
||||
} else {
|
||||
return 0x2; // Yellow
|
||||
}
|
||||
} else {
|
||||
return 0x3; // Red (or Magenta)
|
||||
}
|
||||
} else {
|
||||
if (color.green > 127) {
|
||||
if (color.blue > 127) {
|
||||
return 0x5; // Cyan -> Blue
|
||||
} else {
|
||||
return 0x6; // Green
|
||||
}
|
||||
} else {
|
||||
if (color.blue > 127) {
|
||||
return 0x5; // Blue
|
||||
} else {
|
||||
return 0x0; // Black
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EPaper6Color::fill(Color color) {
|
||||
uint8_t pixel_color;
|
||||
if (color.is_on()) {
|
||||
pixel_color = color_to_hex(color);
|
||||
} else {
|
||||
pixel_color = 0x1;
|
||||
}
|
||||
|
||||
// We store 8 bitset<3> in 3 bytes
|
||||
// | byte 1 | byte 2 | byte 3 |
|
||||
// |aaabbbaa|abbbaaab|bbaaabbb|
|
||||
uint8_t byte_1 = pixel_color << 5 | pixel_color << 2 | pixel_color >> 1;
|
||||
uint8_t byte_2 = pixel_color << 7 | pixel_color << 4 | pixel_color << 1 | pixel_color >> 2;
|
||||
uint8_t byte_3 = pixel_color << 6 | pixel_color << 3 | pixel_color << 0;
|
||||
|
||||
const size_t buffer_length = this->get_buffer_length();
|
||||
for (size_t i = 0; i < buffer_length; i += 3) {
|
||||
this->buffer_[i + 0] = byte_1;
|
||||
this->buffer_[i + 1] = byte_2;
|
||||
this->buffer_[i + 2] = byte_3;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t EPaper6Color::get_buffer_length() {
|
||||
// 6 colors buffer, 1 pixel = 3 bits, we will store 8 pixels in 24 bits = 3 bytes
|
||||
return this->get_width_controller() * this->get_height_internal() / 8u * 3u;
|
||||
}
|
||||
|
||||
void HOT EPaper6Color::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0)
|
||||
return;
|
||||
|
||||
uint8_t pixel_bits = color_to_hex(color);
|
||||
uint32_t pixel_position = x + y * this->get_width_controller();
|
||||
uint32_t first_bit_position = pixel_position * 3;
|
||||
uint32_t byte_position = first_bit_position / 8u;
|
||||
uint32_t byte_subposition = first_bit_position % 8u;
|
||||
|
||||
if (byte_subposition <= 5) {
|
||||
this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 << (5 - byte_subposition)))) |
|
||||
(pixel_bits << (5 - byte_subposition));
|
||||
} else {
|
||||
this->buffer_[byte_position] = (this->buffer_[byte_position] & (0xFF ^ (0b111 >> (byte_subposition - 5)))) |
|
||||
(pixel_bits >> (byte_subposition - 5));
|
||||
|
||||
this->buffer_[byte_position + 1] =
|
||||
(this->buffer_[byte_position + 1] & (0xFF ^ (0xFF & (0b111 << (13 - byte_subposition))))) |
|
||||
(pixel_bits << (13 - byte_subposition));
|
||||
}
|
||||
}
|
||||
|
||||
bool HOT EPaper6Color::transfer_data() {
|
||||
const uint32_t start_time = App.get_loop_component_start_time();
|
||||
if (this->current_data_index_ == 0) {
|
||||
ESP_LOGI(TAG, "Sending data to the display");
|
||||
this->command(0x10);
|
||||
}
|
||||
|
||||
uint8_t bytes_to_send[4]{0};
|
||||
const size_t buffer_length = this->get_buffer_length();
|
||||
for (size_t i = this->current_data_index_; i < buffer_length; i += 3) {
|
||||
const uint32_t triplet = encode_uint24(this->buffer_[i + 0], this->buffer_[i + 1], this->buffer_[i + 2]);
|
||||
// 8 pixels are stored in 3 bytes
|
||||
// |aaabbbaa|abbbaaab|bbaaabbb|
|
||||
// | byte 1 | byte 2 | byte 3 |
|
||||
bytes_to_send[0] = ((triplet >> 17) & 0b01110000) | ((triplet >> 18) & 0b00000111);
|
||||
bytes_to_send[1] = ((triplet >> 11) & 0b01110000) | ((triplet >> 12) & 0b00000111);
|
||||
bytes_to_send[2] = ((triplet >> 5) & 0b01110000) | ((triplet >> 6) & 0b00000111);
|
||||
bytes_to_send[3] = ((triplet << 1) & 0b01110000) | ((triplet << 0) & 0b00000111);
|
||||
|
||||
this->start_data_();
|
||||
this->write_array(bytes_to_send, sizeof(bytes_to_send));
|
||||
this->end_data_();
|
||||
|
||||
if (millis() - start_time > MAX_TRANSFER_TIME) {
|
||||
// Let the main loop run and come back next loop
|
||||
this->current_data_index_ = i + 3;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Finished the entire dataset
|
||||
this->current_data_index_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void EPaper6Color::reset() {
|
||||
if (this->reset_pin_ != nullptr) {
|
||||
this->disable_loop();
|
||||
this->reset_pin_->digital_write(true);
|
||||
this->set_timeout(20, [this] {
|
||||
this->reset_pin_->digital_write(false);
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true);
|
||||
this->set_timeout(20, [this] { this->enable_loop(); });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::epaper_spi
|
23
esphome/components/epaper_spi/epaper_spi_base_6_color.h
Normal file
23
esphome/components/epaper_spi/epaper_spi_base_6_color.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "epaper_spi.h"
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
class EPaper6Color : public EPaperBase {
|
||||
public:
|
||||
EPaper6Color(const uint8_t *init_sequence, const size_t init_sequence_length)
|
||||
: EPaperBase(init_sequence, init_sequence_length) {}
|
||||
|
||||
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
||||
void fill(Color color) override;
|
||||
|
||||
protected:
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
uint32_t get_buffer_length() override;
|
||||
|
||||
bool transfer_data() override;
|
||||
void reset() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::epaper_spi
|
41
esphome/components/epaper_spi/epaper_spi_model_7p3in_e.cpp
Normal file
41
esphome/components/epaper_spi/epaper_spi_model_7p3in_e.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "epaper_spi_model_7p3in_e.h"
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
static constexpr const char *const TAG = "epaper_spi.7.3in-e";
|
||||
|
||||
void EPaper7p3InE::power_on() {
|
||||
ESP_LOGI(TAG, "Power on the display");
|
||||
this->command(0x04);
|
||||
this->waiting_for_idle_ = true;
|
||||
}
|
||||
|
||||
void EPaper7p3InE::power_off() {
|
||||
ESP_LOGI(TAG, "Power off the display");
|
||||
this->command(0x02);
|
||||
this->data(0x00);
|
||||
this->waiting_for_idle_ = true;
|
||||
}
|
||||
|
||||
void EPaper7p3InE::refresh_screen() {
|
||||
this->command(0x12);
|
||||
this->data(0x00);
|
||||
this->waiting_for_idle_ = true;
|
||||
}
|
||||
|
||||
void EPaper7p3InE::deep_sleep() {
|
||||
ESP_LOGI(TAG, "Set the display to deep sleep");
|
||||
this->command(0x07);
|
||||
this->data(0xA5);
|
||||
}
|
||||
|
||||
void EPaper7p3InE::dump_config() {
|
||||
LOG_DISPLAY("", "E-Paper SPI", this);
|
||||
ESP_LOGCONFIG(TAG, " Model: 7.3in-E");
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace esphome::epaper_spi
|
45
esphome/components/epaper_spi/epaper_spi_model_7p3in_e.h
Normal file
45
esphome/components/epaper_spi/epaper_spi_model_7p3in_e.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "epaper_spi_base_6_color.h"
|
||||
|
||||
namespace esphome::epaper_spi {
|
||||
|
||||
class EPaper7p3InE : public EPaper6Color {
|
||||
static constexpr const uint16_t WIDTH = 800;
|
||||
static constexpr const uint16_t HEIGHT = 480;
|
||||
// clang-format off
|
||||
|
||||
// Command, data length, data
|
||||
static constexpr uint8_t INIT_SEQUENCE[] = {
|
||||
0xAA, 6, 0x49, 0x55, 0x20, 0x08, 0x09, 0x18,
|
||||
0x01, 1, 0x3F,
|
||||
0x00, 2, 0x5F, 0x69,
|
||||
0x03, 4, 0x00, 0x54, 0x00, 0x44,
|
||||
0x05, 4, 0x40, 0x1F, 0x1F, 0x2C,
|
||||
0x06, 4, 0x6F, 0x1F, 0x17, 0x49,
|
||||
0x08, 4, 0x6F, 0x1F, 0x1F, 0x22,
|
||||
0x30, 1, 0x03,
|
||||
0x50, 1, 0x3F,
|
||||
0x60, 2, 0x02, 0x00,
|
||||
0x61, 4, WIDTH / 256, WIDTH % 256, HEIGHT / 256, HEIGHT % 256,
|
||||
0x84, 1, 0x01,
|
||||
0xE3, 1, 0x2F,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
public:
|
||||
EPaper7p3InE() : EPaper6Color(INIT_SEQUENCE, sizeof(INIT_SEQUENCE)) {}
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
int get_width_internal() override { return WIDTH; };
|
||||
int get_height_internal() override { return HEIGHT; };
|
||||
|
||||
void refresh_screen() override;
|
||||
void power_on() override;
|
||||
void power_off() override;
|
||||
void deep_sleep() override;
|
||||
};
|
||||
|
||||
} // namespace esphome::epaper_spi
|
Reference in New Issue
Block a user