mirror of
https://github.com/esphome/esphome.git
synced 2025-08-02 08:27:47 +00:00
Merge branch 'dev' into loop_runtime_stats
This commit is contained in:
commit
cfdb0925ce
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -56,16 +56,14 @@ jobs:
|
|||||||
uses: actions/setup-python@v5.6.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
|
||||||
env:
|
|
||||||
ESPHOME_NO_VENV: 1
|
|
||||||
run: script/setup
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |-
|
run: |-
|
||||||
pip3 install build
|
pip3 install build
|
||||||
python3 -m build
|
python3 -m build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.4
|
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||||
|
with:
|
||||||
|
skip-existing: true
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform.arch }}
|
name: Build ESPHome ${{ matrix.platform.arch }}
|
||||||
|
@ -282,6 +282,7 @@ esphome/components/microphone/* @jesserockz @kahrendt
|
|||||||
esphome/components/mics_4514/* @jesserockz
|
esphome/components/mics_4514/* @jesserockz
|
||||||
esphome/components/midea/* @dudanov
|
esphome/components/midea/* @dudanov
|
||||||
esphome/components/midea_ir/* @dudanov
|
esphome/components/midea_ir/* @dudanov
|
||||||
|
esphome/components/mipi_spi/* @clydebarrow
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
esphome/components/mixer/speaker/* @kahrendt
|
esphome/components/mixer/speaker/* @kahrendt
|
||||||
esphome/components/mlx90393/* @functionpointer
|
esphome/components/mlx90393/* @functionpointer
|
||||||
|
@ -171,7 +171,7 @@ AudioDecoderState AudioDecoder::decode(bool stop_gracefully) {
|
|||||||
|
|
||||||
bytes_available_before_processing = this->input_transfer_buffer_->available();
|
bytes_available_before_processing = this->input_transfer_buffer_->available();
|
||||||
|
|
||||||
if ((this->potentially_failed_count_ > 10) && (bytes_read == 0)) {
|
if ((this->potentially_failed_count_ > 0) && (bytes_read == 0)) {
|
||||||
// Failed to decode in last attempt and there is no new data
|
// Failed to decode in last attempt and there is no new data
|
||||||
|
|
||||||
if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
|
if ((this->input_transfer_buffer_->free() == 0) && first_loop_iteration) {
|
||||||
|
@ -1,31 +1,22 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
|
||||||
|
|
||||||
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
from .. import BEDJET_CLIENT_SCHEMA, bedjet_ns, register_bedjet_child
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
CODEOWNERS = ["@jhansche"]
|
CODEOWNERS = ["@jhansche"]
|
||||||
DEPENDENCIES = ["bedjet"]
|
DEPENDENCIES = ["bedjet"]
|
||||||
|
|
||||||
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
|
BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
fan.FAN_SCHEMA.extend(
|
fan.fan_schema(BedJetFan)
|
||||||
{
|
|
||||||
cv.GenerateID(): cv.declare_id(BedJetFan),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
.extend(BEDJET_CLIENT_SCHEMA)
|
.extend(BEDJET_CLIENT_SCHEMA)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
await register_bedjet_child(var, config)
|
await register_bedjet_child(var, config)
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan, output
|
from esphome.components import fan, output
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import CONF_DIRECTION_OUTPUT, CONF_OSCILLATION_OUTPUT, CONF_OUTPUT
|
||||||
CONF_DIRECTION_OUTPUT,
|
|
||||||
CONF_OSCILLATION_OUTPUT,
|
|
||||||
CONF_OUTPUT,
|
|
||||||
CONF_OUTPUT_ID,
|
|
||||||
)
|
|
||||||
|
|
||||||
from .. import binary_ns
|
from .. import binary_ns
|
||||||
|
|
||||||
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
|
BinaryFan = binary_ns.class_("BinaryFan", fan.Fan, cg.Component)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
|
fan.fan_schema(BinaryFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(BinaryFan),
|
|
||||||
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
@ -51,35 +51,60 @@ bool BluetoothProxy::parse_device(const esp32_ble_tracker::ESPBTDevice &device)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr size_t FLUSH_BATCH_SIZE = 8;
|
||||||
|
static std::vector<api::BluetoothLERawAdvertisement> &get_batch_buffer() {
|
||||||
|
static std::vector<api::BluetoothLERawAdvertisement> batch_buffer;
|
||||||
|
return batch_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
bool BluetoothProxy::parse_devices(esp_ble_gap_cb_param_t::ble_scan_result_evt_param *advertisements, size_t count) {
|
||||||
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
if (!api::global_api_server->is_connected() || this->api_connection_ == nullptr || !this->raw_advertisements_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
api::BluetoothLERawAdvertisementsResponse resp;
|
// Get the batch buffer reference
|
||||||
// Pre-allocate the advertisements vector to avoid reallocations
|
auto &batch_buffer = get_batch_buffer();
|
||||||
resp.advertisements.reserve(count);
|
|
||||||
|
|
||||||
|
// Reserve additional capacity if needed
|
||||||
|
size_t new_size = batch_buffer.size() + count;
|
||||||
|
if (batch_buffer.capacity() < new_size) {
|
||||||
|
batch_buffer.reserve(new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new advertisements to the batch buffer
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
auto &result = advertisements[i];
|
auto &result = advertisements[i];
|
||||||
api::BluetoothLERawAdvertisement adv;
|
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
||||||
|
|
||||||
|
batch_buffer.emplace_back();
|
||||||
|
auto &adv = batch_buffer.back();
|
||||||
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
adv.address = esp32_ble::ble_addr_to_uint64(result.bda);
|
||||||
adv.rssi = result.rssi;
|
adv.rssi = result.rssi;
|
||||||
adv.address_type = result.ble_addr_type;
|
adv.address_type = result.ble_addr_type;
|
||||||
|
adv.data.assign(&result.ble_adv[0], &result.ble_adv[length]);
|
||||||
|
|
||||||
uint8_t length = result.adv_data_len + result.scan_rsp_len;
|
ESP_LOGV(TAG, "Queuing raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
||||||
adv.data.reserve(length);
|
|
||||||
// Use a bulk insert instead of individual push_backs
|
|
||||||
adv.data.insert(adv.data.end(), &result.ble_adv[0], &result.ble_adv[length]);
|
|
||||||
|
|
||||||
resp.advertisements.push_back(std::move(adv));
|
|
||||||
|
|
||||||
ESP_LOGV(TAG, "Proxying raw packet from %02X:%02X:%02X:%02X:%02X:%02X, length %d. RSSI: %d dB", result.bda[0],
|
|
||||||
result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
|
result.bda[1], result.bda[2], result.bda[3], result.bda[4], result.bda[5], length, result.rssi);
|
||||||
}
|
}
|
||||||
ESP_LOGV(TAG, "Proxying %d packets", count);
|
|
||||||
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
// Only send if we've accumulated a good batch size to maximize batching efficiency
|
||||||
|
// https://github.com/esphome/backlog/issues/21
|
||||||
|
if (batch_buffer.size() >= FLUSH_BATCH_SIZE) {
|
||||||
|
this->flush_pending_advertisements();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BluetoothProxy::flush_pending_advertisements() {
|
||||||
|
auto &batch_buffer = get_batch_buffer();
|
||||||
|
if (batch_buffer.empty() || !api::global_api_server->is_connected() || this->api_connection_ == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
api::BluetoothLERawAdvertisementsResponse resp;
|
||||||
|
resp.advertisements.swap(batch_buffer);
|
||||||
|
this->api_connection_->send_bluetooth_le_raw_advertisements_response(resp);
|
||||||
|
}
|
||||||
|
|
||||||
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
api::BluetoothLEAdvertisementResponse resp;
|
api::BluetoothLEAdvertisementResponse resp;
|
||||||
resp.address = device.address_uint64();
|
resp.address = device.address_uint64();
|
||||||
@ -91,28 +116,28 @@ void BluetoothProxy::send_api_packet_(const esp32_ble_tracker::ESPBTDevice &devi
|
|||||||
// Pre-allocate vectors based on known sizes
|
// Pre-allocate vectors based on known sizes
|
||||||
auto service_uuids = device.get_service_uuids();
|
auto service_uuids = device.get_service_uuids();
|
||||||
resp.service_uuids.reserve(service_uuids.size());
|
resp.service_uuids.reserve(service_uuids.size());
|
||||||
for (auto uuid : service_uuids) {
|
for (auto &uuid : service_uuids) {
|
||||||
resp.service_uuids.push_back(uuid.to_string());
|
resp.service_uuids.emplace_back(uuid.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-allocate service data vector
|
// Pre-allocate service data vector
|
||||||
auto service_datas = device.get_service_datas();
|
auto service_datas = device.get_service_datas();
|
||||||
resp.service_data.reserve(service_datas.size());
|
resp.service_data.reserve(service_datas.size());
|
||||||
for (auto &data : service_datas) {
|
for (auto &data : service_datas) {
|
||||||
api::BluetoothServiceData service_data;
|
resp.service_data.emplace_back();
|
||||||
|
auto &service_data = resp.service_data.back();
|
||||||
service_data.uuid = data.uuid.to_string();
|
service_data.uuid = data.uuid.to_string();
|
||||||
service_data.data.assign(data.data.begin(), data.data.end());
|
service_data.data.assign(data.data.begin(), data.data.end());
|
||||||
resp.service_data.push_back(std::move(service_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pre-allocate manufacturer data vector
|
// Pre-allocate manufacturer data vector
|
||||||
auto manufacturer_datas = device.get_manufacturer_datas();
|
auto manufacturer_datas = device.get_manufacturer_datas();
|
||||||
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
resp.manufacturer_data.reserve(manufacturer_datas.size());
|
||||||
for (auto &data : manufacturer_datas) {
|
for (auto &data : manufacturer_datas) {
|
||||||
api::BluetoothServiceData manufacturer_data;
|
resp.manufacturer_data.emplace_back();
|
||||||
|
auto &manufacturer_data = resp.manufacturer_data.back();
|
||||||
manufacturer_data.uuid = data.uuid.to_string();
|
manufacturer_data.uuid = data.uuid.to_string();
|
||||||
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
manufacturer_data.data.assign(data.data.begin(), data.data.end());
|
||||||
resp.manufacturer_data.push_back(std::move(manufacturer_data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
this->api_connection_->send_bluetooth_le_advertisement(resp);
|
||||||
@ -148,6 +173,18 @@ void BluetoothProxy::loop() {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush any pending BLE advertisements that have been accumulated but not yet sent
|
||||||
|
if (this->raw_advertisements_) {
|
||||||
|
static uint32_t last_flush_time = 0;
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
// Flush accumulated advertisements every 100ms
|
||||||
|
if (now - last_flush_time >= 100) {
|
||||||
|
this->flush_pending_advertisements();
|
||||||
|
last_flush_time = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (auto *connection : this->connections_) {
|
for (auto *connection : this->connections_) {
|
||||||
if (connection->send_service_ == connection->service_count_) {
|
if (connection->send_service_ == connection->service_count_) {
|
||||||
connection->send_service_ = DONE_SENDING_SERVICES;
|
connection->send_service_ = DONE_SENDING_SERVICES;
|
||||||
|
@ -56,6 +56,7 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
void flush_pending_advertisements();
|
||||||
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
esp32_ble_tracker::AdvertisementParserType get_advertisement_parser_type() override;
|
||||||
|
|
||||||
void register_connection(BluetoothConnection *connection) {
|
void register_connection(BluetoothConnection *connection) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ENTITY_CATEGORY, CONF_ICON, CONF_ID, CONF_SOURCE_ID
|
from esphome.const import CONF_ENTITY_CATEGORY, CONF_ICON, CONF_SOURCE_ID
|
||||||
from esphome.core.entity_helpers import inherit_property_from
|
from esphome.core.entity_helpers import inherit_property_from
|
||||||
|
|
||||||
from .. import copy_ns
|
from .. import copy_ns
|
||||||
@ -9,12 +9,15 @@ from .. import copy_ns
|
|||||||
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
CopyFan = copy_ns.class_("CopyFan", fan.Fan, cg.Component)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
|
fan.fan_schema(CopyFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(CopyFan),
|
|
||||||
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
cv.Required(CONF_SOURCE_ID): cv.use_id(fan.Fan),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||||
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
inherit_property_from(CONF_ICON, CONF_SOURCE_ID),
|
||||||
@ -23,8 +26,7 @@ FINAL_VALIDATE_SCHEMA = cv.All(
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await fan.new_fan(config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
source = await cg.get_variable(config[CONF_SOURCE_ID])
|
||||||
|
@ -2,6 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import switch
|
from esphome.components import switch
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_TYPE, ENTITY_CATEGORY_CONFIG
|
from esphome.const import CONF_TYPE, ENTITY_CATEGORY_CONFIG
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
|
|
||||||
from .. import CONF_DFROBOT_SEN0395_ID, DfrobotSen0395Component
|
from .. import CONF_DFROBOT_SEN0395_ID, DfrobotSen0395Component
|
||||||
|
|
||||||
@ -26,32 +27,30 @@ Sen0395StartAfterBootSwitch = dfrobot_sen0395_ns.class_(
|
|||||||
"Sen0395StartAfterBootSwitch", DfrobotSen0395Switch
|
"Sen0395StartAfterBootSwitch", DfrobotSen0395Switch
|
||||||
)
|
)
|
||||||
|
|
||||||
_SWITCH_SCHEMA = (
|
|
||||||
|
def _switch_schema(class_: MockObjClass) -> cv.Schema:
|
||||||
|
return (
|
||||||
switch.switch_schema(
|
switch.switch_schema(
|
||||||
|
class_,
|
||||||
entity_category=ENTITY_CATEGORY_CONFIG,
|
entity_category=ENTITY_CATEGORY_CONFIG,
|
||||||
)
|
)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(DfrobotSen0395Component),
|
cv.GenerateID(CONF_DFROBOT_SEN0395_ID): cv.use_id(
|
||||||
|
DfrobotSen0395Component
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.COMPONENT_SCHEMA)
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.typed_schema(
|
CONFIG_SCHEMA = cv.typed_schema(
|
||||||
{
|
{
|
||||||
"sensor_active": _SWITCH_SCHEMA.extend(
|
"sensor_active": _switch_schema(Sen0395PowerSwitch),
|
||||||
{cv.GenerateID(): cv.declare_id(Sen0395PowerSwitch)}
|
"turn_on_led": _switch_schema(Sen0395LedSwitch),
|
||||||
),
|
"presence_via_uart": _switch_schema(Sen0395UartPresenceSwitch),
|
||||||
"turn_on_led": _SWITCH_SCHEMA.extend(
|
"start_after_boot": _switch_schema(Sen0395StartAfterBootSwitch),
|
||||||
{cv.GenerateID(): cv.declare_id(Sen0395LedSwitch)}
|
|
||||||
),
|
|
||||||
"presence_via_uart": _SWITCH_SCHEMA.extend(
|
|
||||||
{cv.GenerateID(): cv.declare_id(Sen0395UartPresenceSwitch)}
|
|
||||||
),
|
|
||||||
"start_after_boot": _SWITCH_SCHEMA.extend(
|
|
||||||
{cv.GenerateID(): cv.declare_id(Sen0395StartAfterBootSwitch)}
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,42 +2,66 @@
|
|||||||
|
|
||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/rtc_io.h"
|
||||||
|
#include "hal/gpio_hal.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
#include "soc/gpio_periph.h"
|
||||||
#include <cinttypes>
|
#include <cinttypes>
|
||||||
|
|
||||||
|
#if (SOC_RTCIO_PIN_COUNT > 0)
|
||||||
|
#include "hal/rtc_io_hal.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SOC_GPIO_SUPPORT_RTC_INDEPENDENT
|
||||||
|
#define SOC_GPIO_SUPPORT_RTC_INDEPENDENT 0 // NOLINT
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace esp32 {
|
namespace esp32 {
|
||||||
|
|
||||||
static const char *const TAG = "esp32";
|
static const char *const TAG = "esp32";
|
||||||
|
|
||||||
|
static const gpio_hal_context_t GPIO_HAL = {.dev = GPIO_HAL_GET_HW(GPIO_PORT_0)};
|
||||||
|
|
||||||
bool ESP32InternalGPIOPin::isr_service_installed = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
bool ESP32InternalGPIOPin::isr_service_installed = false; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
static gpio_mode_t IRAM_ATTR flags_to_mode(gpio::Flags flags) {
|
static gpio_mode_t flags_to_mode(gpio::Flags flags) {
|
||||||
flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN));
|
flags = (gpio::Flags)(flags & ~(gpio::FLAG_PULLUP | gpio::FLAG_PULLDOWN));
|
||||||
if (flags == gpio::FLAG_INPUT) {
|
if (flags == gpio::FLAG_INPUT)
|
||||||
return GPIO_MODE_INPUT;
|
return GPIO_MODE_INPUT;
|
||||||
} else if (flags == gpio::FLAG_OUTPUT) {
|
if (flags == gpio::FLAG_OUTPUT)
|
||||||
return GPIO_MODE_OUTPUT;
|
return GPIO_MODE_OUTPUT;
|
||||||
} else if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
|
if (flags == (gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN))
|
||||||
return GPIO_MODE_OUTPUT_OD;
|
return GPIO_MODE_OUTPUT_OD;
|
||||||
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN)) {
|
if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT | gpio::FLAG_OPEN_DRAIN))
|
||||||
return GPIO_MODE_INPUT_OUTPUT_OD;
|
return GPIO_MODE_INPUT_OUTPUT_OD;
|
||||||
} else if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT)) {
|
if (flags == (gpio::FLAG_INPUT | gpio::FLAG_OUTPUT))
|
||||||
return GPIO_MODE_INPUT_OUTPUT;
|
return GPIO_MODE_INPUT_OUTPUT;
|
||||||
} else {
|
|
||||||
// unsupported or gpio::FLAG_NONE
|
// unsupported or gpio::FLAG_NONE
|
||||||
return GPIO_MODE_DISABLE;
|
return GPIO_MODE_DISABLE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct ISRPinArg {
|
struct ISRPinArg {
|
||||||
gpio_num_t pin;
|
gpio_num_t pin;
|
||||||
|
gpio::Flags flags;
|
||||||
bool inverted;
|
bool inverted;
|
||||||
|
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||||
|
bool use_rtc;
|
||||||
|
int rtc_pin;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const {
|
ISRInternalGPIOPin ESP32InternalGPIOPin::to_isr() const {
|
||||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
arg->pin = pin_;
|
arg->pin = this->pin_;
|
||||||
|
arg->flags = gpio::FLAG_NONE;
|
||||||
arg->inverted = inverted_;
|
arg->inverted = inverted_;
|
||||||
|
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||||
|
arg->use_rtc = rtc_gpio_is_valid_gpio(this->pin_);
|
||||||
|
if (arg->use_rtc)
|
||||||
|
arg->rtc_pin = rtc_io_number_get(this->pin_);
|
||||||
|
#endif
|
||||||
return ISRInternalGPIOPin((void *) arg);
|
return ISRInternalGPIOPin((void *) arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,6 +114,7 @@ void ESP32InternalGPIOPin::setup() {
|
|||||||
if (flags_ & gpio::FLAG_OUTPUT) {
|
if (flags_ & gpio::FLAG_OUTPUT) {
|
||||||
gpio_set_drive_capability(pin_, drive_strength_);
|
gpio_set_drive_capability(pin_, drive_strength_);
|
||||||
}
|
}
|
||||||
|
ESP_LOGD(TAG, "rtc: %d", SOC_GPIO_SUPPORT_RTC_INDEPENDENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {
|
void ESP32InternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||||
@ -115,28 +140,65 @@ void ESP32InternalGPIOPin::detach_interrupt() const { gpio_intr_disable(pin_); }
|
|||||||
using namespace esp32;
|
using namespace esp32;
|
||||||
|
|
||||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_);
|
||||||
return bool(gpio_get_level(arg->pin)) != arg->inverted;
|
return bool(gpio_hal_get_level(&GPIO_HAL, arg->pin)) != arg->inverted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_);
|
||||||
gpio_set_level(arg->pin, value != arg->inverted ? 1 : 0);
|
gpio_hal_set_level(&GPIO_HAL, arg->pin, value != arg->inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||||
// not supported
|
// not supported
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||||
gpio_set_direction(arg->pin, flags_to_mode(flags));
|
gpio::Flags diff = (gpio::Flags)(flags ^ arg->flags);
|
||||||
gpio_pull_mode_t pull_mode = GPIO_FLOATING;
|
if (diff & gpio::FLAG_OUTPUT) {
|
||||||
if ((flags & gpio::FLAG_PULLUP) && (flags & gpio::FLAG_PULLDOWN)) {
|
if (flags & gpio::FLAG_OUTPUT) {
|
||||||
pull_mode = GPIO_PULLUP_PULLDOWN;
|
gpio_hal_output_enable(&GPIO_HAL, arg->pin);
|
||||||
} else if (flags & gpio::FLAG_PULLUP) {
|
if (flags & gpio::FLAG_OPEN_DRAIN)
|
||||||
pull_mode = GPIO_PULLUP_ONLY;
|
gpio_hal_od_enable(&GPIO_HAL, arg->pin);
|
||||||
} else if (flags & gpio::FLAG_PULLDOWN) {
|
} else {
|
||||||
pull_mode = GPIO_PULLDOWN_ONLY;
|
gpio_hal_output_disable(&GPIO_HAL, arg->pin);
|
||||||
}
|
}
|
||||||
gpio_set_pull_mode(arg->pin, pull_mode);
|
}
|
||||||
|
if (diff & gpio::FLAG_INPUT) {
|
||||||
|
if (flags & gpio::FLAG_INPUT) {
|
||||||
|
gpio_hal_input_enable(&GPIO_HAL, arg->pin);
|
||||||
|
#if defined(USE_ESP32_VARIANT_ESP32)
|
||||||
|
if (arg->use_rtc) {
|
||||||
|
if (flags & gpio::FLAG_PULLUP) {
|
||||||
|
rtcio_hal_pullup_enable(arg->rtc_pin);
|
||||||
|
} else {
|
||||||
|
rtcio_hal_pullup_disable(arg->rtc_pin);
|
||||||
|
}
|
||||||
|
if (flags & gpio::FLAG_PULLDOWN) {
|
||||||
|
rtcio_hal_pulldown_enable(arg->rtc_pin);
|
||||||
|
} else {
|
||||||
|
rtcio_hal_pulldown_disable(arg->rtc_pin);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
if (flags & gpio::FLAG_PULLUP) {
|
||||||
|
gpio_hal_pullup_en(&GPIO_HAL, arg->pin);
|
||||||
|
} else {
|
||||||
|
gpio_hal_pullup_dis(&GPIO_HAL, arg->pin);
|
||||||
|
}
|
||||||
|
if (flags & gpio::FLAG_PULLDOWN) {
|
||||||
|
gpio_hal_pulldown_en(&GPIO_HAL, arg->pin);
|
||||||
|
} else {
|
||||||
|
gpio_hal_pulldown_dis(&GPIO_HAL, arg->pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
gpio_hal_input_disable(&GPIO_HAL, arg->pin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
arg->flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Callable
|
||||||
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
@ -64,8 +64,7 @@ def _lookup_pin(value):
|
|||||||
def _translate_pin(value):
|
def _translate_pin(value):
|
||||||
if isinstance(value, dict) or value is None:
|
if isinstance(value, dict) or value is None:
|
||||||
raise cv.Invalid(
|
raise cv.Invalid(
|
||||||
"This variable only supports pin numbers, not full pin schemas "
|
"This variable only supports pin numbers, not full pin schemas (with inverted and mode)."
|
||||||
"(with inverted and mode)."
|
|
||||||
)
|
)
|
||||||
if isinstance(value, int) and not isinstance(value, bool):
|
if isinstance(value, int) and not isinstance(value, bool):
|
||||||
return value
|
return value
|
||||||
@ -82,30 +81,22 @@ def _translate_pin(value):
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ESP32ValidationFunctions:
|
class ESP32ValidationFunctions:
|
||||||
pin_validation: Any
|
pin_validation: Callable[[Any], Any]
|
||||||
usage_validation: Any
|
usage_validation: Callable[[Any], Any]
|
||||||
|
|
||||||
|
|
||||||
_esp32_validations = {
|
_esp32_validations = {
|
||||||
VARIANT_ESP32: ESP32ValidationFunctions(
|
VARIANT_ESP32: ESP32ValidationFunctions(
|
||||||
pin_validation=esp32_validate_gpio_pin, usage_validation=esp32_validate_supports
|
pin_validation=esp32_validate_gpio_pin, usage_validation=esp32_validate_supports
|
||||||
),
|
),
|
||||||
VARIANT_ESP32S2: ESP32ValidationFunctions(
|
VARIANT_ESP32C2: ESP32ValidationFunctions(
|
||||||
pin_validation=esp32_s2_validate_gpio_pin,
|
pin_validation=esp32_c2_validate_gpio_pin,
|
||||||
usage_validation=esp32_s2_validate_supports,
|
usage_validation=esp32_c2_validate_supports,
|
||||||
),
|
),
|
||||||
VARIANT_ESP32C3: ESP32ValidationFunctions(
|
VARIANT_ESP32C3: ESP32ValidationFunctions(
|
||||||
pin_validation=esp32_c3_validate_gpio_pin,
|
pin_validation=esp32_c3_validate_gpio_pin,
|
||||||
usage_validation=esp32_c3_validate_supports,
|
usage_validation=esp32_c3_validate_supports,
|
||||||
),
|
),
|
||||||
VARIANT_ESP32S3: ESP32ValidationFunctions(
|
|
||||||
pin_validation=esp32_s3_validate_gpio_pin,
|
|
||||||
usage_validation=esp32_s3_validate_supports,
|
|
||||||
),
|
|
||||||
VARIANT_ESP32C2: ESP32ValidationFunctions(
|
|
||||||
pin_validation=esp32_c2_validate_gpio_pin,
|
|
||||||
usage_validation=esp32_c2_validate_supports,
|
|
||||||
),
|
|
||||||
VARIANT_ESP32C6: ESP32ValidationFunctions(
|
VARIANT_ESP32C6: ESP32ValidationFunctions(
|
||||||
pin_validation=esp32_c6_validate_gpio_pin,
|
pin_validation=esp32_c6_validate_gpio_pin,
|
||||||
usage_validation=esp32_c6_validate_supports,
|
usage_validation=esp32_c6_validate_supports,
|
||||||
@ -114,6 +105,14 @@ _esp32_validations = {
|
|||||||
pin_validation=esp32_h2_validate_gpio_pin,
|
pin_validation=esp32_h2_validate_gpio_pin,
|
||||||
usage_validation=esp32_h2_validate_supports,
|
usage_validation=esp32_h2_validate_supports,
|
||||||
),
|
),
|
||||||
|
VARIANT_ESP32S2: ESP32ValidationFunctions(
|
||||||
|
pin_validation=esp32_s2_validate_gpio_pin,
|
||||||
|
usage_validation=esp32_s2_validate_supports,
|
||||||
|
),
|
||||||
|
VARIANT_ESP32S3: ESP32ValidationFunctions(
|
||||||
|
pin_validation=esp32_s3_validate_gpio_pin,
|
||||||
|
usage_validation=esp32_s3_validate_supports,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,8 +31,7 @@ def esp32_validate_gpio_pin(value):
|
|||||||
)
|
)
|
||||||
if 9 <= value <= 10:
|
if 9 <= value <= 10:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Pin %s (9-10) might already be used by the "
|
"Pin %s (9-10) might already be used by the flash interface in QUAD IO flash mode.",
|
||||||
"flash interface in QUAD IO flash mode.",
|
|
||||||
value,
|
value,
|
||||||
)
|
)
|
||||||
if value in (24, 28, 29, 30, 31):
|
if value in (24, 28, 29, 30, 31):
|
||||||
|
@ -22,7 +22,7 @@ def esp32_c2_validate_supports(value):
|
|||||||
is_input = mode[CONF_INPUT]
|
is_input = mode[CONF_INPUT]
|
||||||
|
|
||||||
if num < 0 or num > 20:
|
if num < 0 or num > 20:
|
||||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-20)")
|
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-20)")
|
||||||
|
|
||||||
if is_input:
|
if is_input:
|
||||||
# All ESP32 pins support input mode
|
# All ESP32 pins support input mode
|
||||||
|
@ -35,7 +35,7 @@ def esp32_c3_validate_supports(value):
|
|||||||
is_input = mode[CONF_INPUT]
|
is_input = mode[CONF_INPUT]
|
||||||
|
|
||||||
if num < 0 or num > 21:
|
if num < 0 or num > 21:
|
||||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-21)")
|
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-21)")
|
||||||
|
|
||||||
if is_input:
|
if is_input:
|
||||||
# All ESP32 pins support input mode
|
# All ESP32 pins support input mode
|
||||||
|
@ -36,7 +36,7 @@ def esp32_c6_validate_supports(value):
|
|||||||
is_input = mode[CONF_INPUT]
|
is_input = mode[CONF_INPUT]
|
||||||
|
|
||||||
if num < 0 or num > 23:
|
if num < 0 or num > 23:
|
||||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-23)")
|
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-23)")
|
||||||
if is_input:
|
if is_input:
|
||||||
# All ESP32 pins support input mode
|
# All ESP32 pins support input mode
|
||||||
pass
|
pass
|
||||||
|
@ -45,7 +45,7 @@ def esp32_h2_validate_supports(value):
|
|||||||
is_input = mode[CONF_INPUT]
|
is_input = mode[CONF_INPUT]
|
||||||
|
|
||||||
if num < 0 or num > 27:
|
if num < 0 or num > 27:
|
||||||
raise cv.Invalid(f"Invalid pin number: {value} (must be 0-27)")
|
raise cv.Invalid(f"Invalid pin number: {num} (must be 0-27)")
|
||||||
if is_input:
|
if is_input:
|
||||||
# All ESP32 pins support input mode
|
# All ESP32 pins support input mode
|
||||||
pass
|
pass
|
||||||
|
@ -122,7 +122,7 @@ void ESP32BLETracker::loop() {
|
|||||||
|
|
||||||
if (this->scanner_state_ == ScannerState::RUNNING &&
|
if (this->scanner_state_ == ScannerState::RUNNING &&
|
||||||
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
this->scan_result_index_ && // if it looks like we have a scan result we will take the lock
|
||||||
xSemaphoreTake(this->scan_result_lock_, 5L / portTICK_PERIOD_MS)) {
|
xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||||
uint32_t index = this->scan_result_index_;
|
uint32_t index = this->scan_result_index_;
|
||||||
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
if (index >= ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||||
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
ESP_LOGW(TAG, "Too many BLE events to process. Some devices may not show up.");
|
||||||
@ -447,7 +447,7 @@ void ESP32BLETracker::gap_scan_stop_complete_(const esp_ble_gap_cb_param_t::ble_
|
|||||||
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
void ESP32BLETracker::gap_scan_result_(const esp_ble_gap_cb_param_t::ble_scan_result_evt_param ¶m) {
|
||||||
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
ESP_LOGV(TAG, "gap_scan_result - event %d", param.search_evt);
|
||||||
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
if (param.search_evt == ESP_GAP_SEARCH_INQ_RES_EVT) {
|
||||||
if (xSemaphoreTake(this->scan_result_lock_, 0L)) {
|
if (xSemaphoreTake(this->scan_result_lock_, 0)) {
|
||||||
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
if (this->scan_result_index_ < ESP32BLETracker::SCAN_RESULT_BUFFER_SIZE) {
|
||||||
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
this->scan_result_buffer_[this->scan_result_index_++] = param;
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ class ESP32BLETracker : public Component,
|
|||||||
#ifdef USE_PSRAM
|
#ifdef USE_PSRAM
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 32;
|
||||||
#else
|
#else
|
||||||
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 16;
|
const static u_int8_t SCAN_RESULT_BUFFER_SIZE = 20;
|
||||||
#endif // USE_PSRAM
|
#endif // USE_PSRAM
|
||||||
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
esp_ble_gap_cb_param_t::ble_scan_result_evt_param *scan_result_buffer_;
|
||||||
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
esp_bt_status_t scan_start_failed_{ESP_BT_STATUS_SUCCESS};
|
||||||
|
@ -8,7 +8,7 @@ namespace esp8266 {
|
|||||||
|
|
||||||
static const char *const TAG = "esp8266";
|
static const char *const TAG = "esp8266";
|
||||||
|
|
||||||
static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) {
|
static int flags_to_mode(gpio::Flags flags, uint8_t pin) {
|
||||||
if (flags == gpio::FLAG_INPUT) { // NOLINT(bugprone-branch-clone)
|
if (flags == gpio::FLAG_INPUT) { // NOLINT(bugprone-branch-clone)
|
||||||
return INPUT;
|
return INPUT;
|
||||||
} else if (flags == gpio::FLAG_OUTPUT) {
|
} else if (flags == gpio::FLAG_OUTPUT) {
|
||||||
@ -34,12 +34,36 @@ static int IRAM_ATTR flags_to_mode(gpio::Flags flags, uint8_t pin) {
|
|||||||
struct ISRPinArg {
|
struct ISRPinArg {
|
||||||
uint8_t pin;
|
uint8_t pin;
|
||||||
bool inverted;
|
bool inverted;
|
||||||
|
volatile uint32_t *in_reg;
|
||||||
|
volatile uint32_t *out_set_reg;
|
||||||
|
volatile uint32_t *out_clr_reg;
|
||||||
|
volatile uint32_t *mode_set_reg;
|
||||||
|
volatile uint32_t *mode_clr_reg;
|
||||||
|
volatile uint32_t *func_reg;
|
||||||
|
uint32_t mask;
|
||||||
};
|
};
|
||||||
|
|
||||||
ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const {
|
ISRInternalGPIOPin ESP8266GPIOPin::to_isr() const {
|
||||||
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
auto *arg = new ISRPinArg{}; // NOLINT(cppcoreguidelines-owning-memory)
|
||||||
arg->pin = pin_;
|
arg->pin = this->pin_;
|
||||||
arg->inverted = inverted_;
|
arg->inverted = this->inverted_;
|
||||||
|
if (this->pin_ < 16) {
|
||||||
|
arg->in_reg = &GPI;
|
||||||
|
arg->out_set_reg = &GPOS;
|
||||||
|
arg->out_clr_reg = &GPOC;
|
||||||
|
arg->mode_set_reg = &GPES;
|
||||||
|
arg->mode_clr_reg = &GPEC;
|
||||||
|
arg->func_reg = &GPF(this->pin_);
|
||||||
|
arg->mask = 1 << this->pin_;
|
||||||
|
} else {
|
||||||
|
arg->in_reg = &GP16I;
|
||||||
|
arg->out_set_reg = &GP16O;
|
||||||
|
arg->out_clr_reg = nullptr;
|
||||||
|
arg->mode_set_reg = &GP16E;
|
||||||
|
arg->mode_clr_reg = nullptr;
|
||||||
|
arg->func_reg = &GPF16;
|
||||||
|
arg->mask = 1;
|
||||||
|
}
|
||||||
return ISRInternalGPIOPin((void *) arg);
|
return ISRInternalGPIOPin((void *) arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,20 +112,57 @@ void ESP8266GPIOPin::detach_interrupt() const { detachInterrupt(pin_); }
|
|||||||
using namespace esp8266;
|
using namespace esp8266;
|
||||||
|
|
||||||
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
bool IRAM_ATTR ISRInternalGPIOPin::digital_read() {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_);
|
||||||
return bool(digitalRead(arg->pin)) != arg->inverted; // NOLINT
|
return bool(*arg->in_reg & arg->mask) != arg->inverted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
void IRAM_ATTR ISRInternalGPIOPin::digital_write(bool value) {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||||
digitalWrite(arg->pin, value != arg->inverted ? 1 : 0); // NOLINT
|
if (arg->pin < 16) {
|
||||||
|
if (value != arg->inverted) {
|
||||||
|
*arg->out_set_reg = arg->mask;
|
||||||
|
} else {
|
||||||
|
*arg->out_clr_reg = arg->mask;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (value != arg->inverted) {
|
||||||
|
*arg->out_set_reg |= 1;
|
||||||
|
} else {
|
||||||
|
*arg->out_set_reg &= ~1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
void IRAM_ATTR ISRInternalGPIOPin::clear_interrupt() {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
||||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin);
|
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, 1UL << arg->pin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
void IRAM_ATTR ISRInternalGPIOPin::pin_mode(gpio::Flags flags) {
|
||||||
auto *arg = reinterpret_cast<ISRPinArg *>(arg_);
|
auto *arg = reinterpret_cast<ISRPinArg *>(this->arg_);
|
||||||
pinMode(arg->pin, flags_to_mode(flags, arg->pin)); // NOLINT
|
if (arg->pin < 16) {
|
||||||
|
if (flags & gpio::FLAG_OUTPUT) {
|
||||||
|
*arg->mode_set_reg = arg->mask;
|
||||||
|
} else {
|
||||||
|
*arg->mode_clr_reg = arg->mask;
|
||||||
|
}
|
||||||
|
if (flags & gpio::FLAG_PULLUP) {
|
||||||
|
*arg->func_reg |= 1 << GPFPU;
|
||||||
|
} else {
|
||||||
|
*arg->func_reg &= ~(1 << GPFPU);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (flags & gpio::FLAG_OUTPUT) {
|
||||||
|
*arg->mode_set_reg |= 1;
|
||||||
|
} else {
|
||||||
|
*arg->mode_set_reg &= ~1;
|
||||||
|
}
|
||||||
|
if (flags & gpio::FLAG_PULLDOWN) {
|
||||||
|
*arg->func_reg |= 1 << GP16FPD;
|
||||||
|
} else {
|
||||||
|
*arg->func_reg &= ~(1 << GP16FPD);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@ -30,9 +30,10 @@ DECAY_MODE_OPTIONS = {
|
|||||||
# Actions
|
# Actions
|
||||||
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
BrakeAction = hbridge_ns.class_("BrakeAction", automation.Action)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
|
fan.fan_schema(HBridgeFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_ID): cv.declare_id(HBridgeFan),
|
|
||||||
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
|
cv.Required(CONF_PIN_A): cv.use_id(output.FloatOutput),
|
||||||
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
|
cv.Required(CONF_PIN_B): cv.use_id(output.FloatOutput),
|
||||||
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
cv.Optional(CONF_DECAY_MODE, default="SLOW"): cv.enum(
|
||||||
@ -42,13 +43,15 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
|||||||
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
cv.Optional(CONF_ENABLE_PIN): cv.use_id(output.FloatOutput),
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"fan.hbridge.brake",
|
"fan.hbridge.brake",
|
||||||
BrakeAction,
|
BrakeAction,
|
||||||
maybe_simple_id({cv.Required(CONF_ID): cv.use_id(HBridgeFan)}),
|
maybe_simple_id({cv.GenerateID(): cv.use_id(HBridgeFan)}),
|
||||||
)
|
)
|
||||||
async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
||||||
paren = await cg.get_variable(config[CONF_ID])
|
paren = await cg.get_variable(config[CONF_ID])
|
||||||
@ -56,13 +59,12 @@ async def fan_hbridge_brake_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(
|
var = await fan.new_fan(
|
||||||
config[CONF_ID],
|
config,
|
||||||
config[CONF_SPEED_COUNT],
|
config[CONF_SPEED_COUNT],
|
||||||
config[CONF_DECAY_MODE],
|
config[CONF_DECAY_MODE],
|
||||||
)
|
)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
pin_a_ = await cg.get_variable(config[CONF_PIN_A])
|
||||||
cg.add(var.set_pin_a(pin_a_))
|
cg.add(var.set_pin_a(pin_a_))
|
||||||
pin_b_ = await cg.get_variable(config[CONF_PIN_B])
|
pin_b_ = await cg.get_variable(config[CONF_PIN_B])
|
||||||
|
@ -2,7 +2,7 @@ from esphome import pins
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import esp32, media_player
|
from esphome.components import esp32, media_player
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_MODE
|
from esphome.const import CONF_MODE
|
||||||
|
|
||||||
from .. import (
|
from .. import (
|
||||||
CONF_I2S_AUDIO_ID,
|
CONF_I2S_AUDIO_ID,
|
||||||
@ -57,16 +57,17 @@ def validate_esp32_variant(config):
|
|||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.typed_schema(
|
cv.typed_schema(
|
||||||
{
|
{
|
||||||
"internal": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
"internal": media_player.media_player_schema(I2SAudioMediaPlayer)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
|
||||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||||
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
|
cv.Required(CONF_MODE): cv.enum(INTERNAL_DAC_OPTIONS, lower=True),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
"external": media_player.MEDIA_PLAYER_SCHEMA.extend(
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
|
"external": media_player.media_player_schema(I2SAudioMediaPlayer)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(I2SAudioMediaPlayer),
|
|
||||||
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
cv.GenerateID(CONF_I2S_AUDIO_ID): cv.use_id(I2SAudioComponent),
|
||||||
cv.Required(
|
cv.Required(
|
||||||
CONF_I2S_DOUT_PIN
|
CONF_I2S_DOUT_PIN
|
||||||
@ -79,7 +80,8 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
*I2C_COMM_FMT_OPTIONS, lower=True
|
*I2C_COMM_FMT_OPTIONS, lower=True
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA),
|
||||||
},
|
},
|
||||||
key=CONF_DAC_TYPE,
|
key=CONF_DAC_TYPE,
|
||||||
),
|
),
|
||||||
@ -97,9 +99,8 @@ FINAL_VALIDATE_SCHEMA = _final_validate
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await media_player.new_media_player(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await media_player.register_media_player(var, config)
|
|
||||||
|
|
||||||
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
|
await cg.register_parented(var, config[CONF_I2S_AUDIO_ID])
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ from esphome import automation
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_ENTITY_CATEGORY,
|
||||||
|
CONF_ICON,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_ON_IDLE,
|
CONF_ON_IDLE,
|
||||||
CONF_ON_STATE,
|
CONF_ON_STATE,
|
||||||
@ -10,6 +12,7 @@ from esphome.const import (
|
|||||||
)
|
)
|
||||||
from esphome.core import CORE
|
from esphome.core import CORE
|
||||||
from esphome.coroutine import coroutine_with_priority
|
from esphome.coroutine import coroutine_with_priority
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
CODEOWNERS = ["@jesserockz"]
|
CODEOWNERS = ["@jesserockz"]
|
||||||
@ -103,7 +106,13 @@ async def register_media_player(var, config):
|
|||||||
await setup_media_player_core_(var, config)
|
await setup_media_player_core_(var, config)
|
||||||
|
|
||||||
|
|
||||||
MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
async def new_media_player(config, *args):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], *args)
|
||||||
|
await register_media_player(var, config)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
_MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
cv.Optional(CONF_ON_STATE): automation.validate_automation(
|
||||||
{
|
{
|
||||||
@ -134,6 +143,29 @@ MEDIA_PLAYER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def media_player_schema(
|
||||||
|
class_: MockObjClass,
|
||||||
|
*,
|
||||||
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
icon: str = cv.UNDEFINED,
|
||||||
|
) -> cv.Schema:
|
||||||
|
schema = {cv.GenerateID(CONF_ID): cv.declare_id(class_)}
|
||||||
|
|
||||||
|
for key, default, validator in [
|
||||||
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
|
(CONF_ICON, icon, cv.icon),
|
||||||
|
]:
|
||||||
|
if default is not cv.UNDEFINED:
|
||||||
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
|
|
||||||
|
return _MEDIA_PLAYER_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
# Remove before 2025.11.0
|
||||||
|
MEDIA_PLAYER_SCHEMA = media_player_schema(MediaPlayer)
|
||||||
|
MEDIA_PLAYER_SCHEMA.add_extra(cv.deprecated_schema_constant("media_player"))
|
||||||
|
|
||||||
|
|
||||||
MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id(
|
MEDIA_PLAYER_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
15
esphome/components/mipi_spi/__init__.py
Normal file
15
esphome/components/mipi_spi/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
CODEOWNERS = ["@clydebarrow"]
|
||||||
|
|
||||||
|
DOMAIN = "mipi_spi"
|
||||||
|
|
||||||
|
CONF_DRAW_FROM_ORIGIN = "draw_from_origin"
|
||||||
|
CONF_SPI_16 = "spi_16"
|
||||||
|
CONF_PIXEL_MODE = "pixel_mode"
|
||||||
|
CONF_COLOR_DEPTH = "color_depth"
|
||||||
|
CONF_BUS_MODE = "bus_mode"
|
||||||
|
CONF_USE_AXIS_FLIPS = "use_axis_flips"
|
||||||
|
CONF_NATIVE_WIDTH = "native_width"
|
||||||
|
CONF_NATIVE_HEIGHT = "native_height"
|
||||||
|
|
||||||
|
MODE_RGB = "RGB"
|
||||||
|
MODE_BGR = "BGR"
|
474
esphome/components/mipi_spi/display.py
Normal file
474
esphome/components/mipi_spi/display.py
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import display, spi
|
||||||
|
from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.config_validation import ALLOW_EXTRA
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BRIGHTNESS,
|
||||||
|
CONF_COLOR_ORDER,
|
||||||
|
CONF_CS_PIN,
|
||||||
|
CONF_DATA_RATE,
|
||||||
|
CONF_DC_PIN,
|
||||||
|
CONF_DIMENSIONS,
|
||||||
|
CONF_ENABLE_PIN,
|
||||||
|
CONF_HEIGHT,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_INIT_SEQUENCE,
|
||||||
|
CONF_INVERT_COLORS,
|
||||||
|
CONF_LAMBDA,
|
||||||
|
CONF_MIRROR_X,
|
||||||
|
CONF_MIRROR_Y,
|
||||||
|
CONF_MODEL,
|
||||||
|
CONF_OFFSET_HEIGHT,
|
||||||
|
CONF_OFFSET_WIDTH,
|
||||||
|
CONF_RESET_PIN,
|
||||||
|
CONF_ROTATION,
|
||||||
|
CONF_SWAP_XY,
|
||||||
|
CONF_TRANSFORM,
|
||||||
|
CONF_WIDTH,
|
||||||
|
)
|
||||||
|
from esphome.core import TimePeriod
|
||||||
|
|
||||||
|
from ..const import CONF_DRAW_ROUNDING
|
||||||
|
from ..lvgl.defines import CONF_COLOR_DEPTH
|
||||||
|
from . import (
|
||||||
|
CONF_BUS_MODE,
|
||||||
|
CONF_DRAW_FROM_ORIGIN,
|
||||||
|
CONF_NATIVE_HEIGHT,
|
||||||
|
CONF_NATIVE_WIDTH,
|
||||||
|
CONF_PIXEL_MODE,
|
||||||
|
CONF_SPI_16,
|
||||||
|
CONF_USE_AXIS_FLIPS,
|
||||||
|
DOMAIN,
|
||||||
|
MODE_BGR,
|
||||||
|
MODE_RGB,
|
||||||
|
)
|
||||||
|
from .models import (
|
||||||
|
DELAY_FLAG,
|
||||||
|
MADCTL_BGR,
|
||||||
|
MADCTL_MV,
|
||||||
|
MADCTL_MX,
|
||||||
|
MADCTL_MY,
|
||||||
|
MADCTL_XFLIP,
|
||||||
|
MADCTL_YFLIP,
|
||||||
|
DriverChip,
|
||||||
|
amoled,
|
||||||
|
cyd,
|
||||||
|
ili,
|
||||||
|
jc,
|
||||||
|
lanbon,
|
||||||
|
lilygo,
|
||||||
|
waveshare,
|
||||||
|
)
|
||||||
|
from .models.commands import BRIGHTNESS, DISPON, INVOFF, INVON, MADCTL, PIXFMT, SLPOUT
|
||||||
|
|
||||||
|
DEPENDENCIES = ["spi"]
|
||||||
|
|
||||||
|
LOGGER = logging.getLogger(DOMAIN)
|
||||||
|
mipi_spi_ns = cg.esphome_ns.namespace("mipi_spi")
|
||||||
|
MipiSpi = mipi_spi_ns.class_(
|
||||||
|
"MipiSpi", display.Display, display.DisplayBuffer, cg.Component, spi.SPIDevice
|
||||||
|
)
|
||||||
|
ColorOrder = display.display_ns.enum("ColorMode")
|
||||||
|
ColorBitness = display.display_ns.enum("ColorBitness")
|
||||||
|
Model = mipi_spi_ns.enum("Model")
|
||||||
|
|
||||||
|
COLOR_ORDERS = {
|
||||||
|
MODE_RGB: ColorOrder.COLOR_ORDER_RGB,
|
||||||
|
MODE_BGR: ColorOrder.COLOR_ORDER_BGR,
|
||||||
|
}
|
||||||
|
|
||||||
|
COLOR_DEPTHS = {
|
||||||
|
8: ColorBitness.COLOR_BITNESS_332,
|
||||||
|
16: ColorBitness.COLOR_BITNESS_565,
|
||||||
|
}
|
||||||
|
DATA_PIN_SCHEMA = pins.internal_gpio_output_pin_schema
|
||||||
|
|
||||||
|
|
||||||
|
DriverChip("CUSTOM", initsequence={})
|
||||||
|
|
||||||
|
MODELS = DriverChip.models
|
||||||
|
# These statements are noops, but serve to suppress linting of side-effect-only imports
|
||||||
|
for _ in (ili, jc, amoled, lilygo, lanbon, cyd, waveshare):
|
||||||
|
pass
|
||||||
|
|
||||||
|
PixelMode = mipi_spi_ns.enum("PixelMode")
|
||||||
|
|
||||||
|
PIXEL_MODE_18BIT = "18bit"
|
||||||
|
PIXEL_MODE_16BIT = "16bit"
|
||||||
|
|
||||||
|
PIXEL_MODES = {
|
||||||
|
PIXEL_MODE_16BIT: 0x55,
|
||||||
|
PIXEL_MODE_18BIT: 0x66,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def validate_dimension(rounding):
|
||||||
|
def validator(value):
|
||||||
|
value = cv.positive_int(value)
|
||||||
|
if value % rounding != 0:
|
||||||
|
raise cv.Invalid(f"Dimensions and offsets must be divisible by {rounding}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
return validator
|
||||||
|
|
||||||
|
|
||||||
|
def map_sequence(value):
|
||||||
|
"""
|
||||||
|
The format is a repeated sequence of [CMD, <data>] where <data> is s a sequence of bytes. The length is inferred
|
||||||
|
from the length of the sequence and should not be explicit.
|
||||||
|
A delay can be inserted by specifying "- delay N" where N is in ms
|
||||||
|
"""
|
||||||
|
if isinstance(value, str) and value.lower().startswith("delay "):
|
||||||
|
value = value.lower()[6:]
|
||||||
|
delay = cv.All(
|
||||||
|
cv.positive_time_period_milliseconds,
|
||||||
|
cv.Range(TimePeriod(milliseconds=1), TimePeriod(milliseconds=255)),
|
||||||
|
)(value)
|
||||||
|
return DELAY_FLAG, delay.total_milliseconds
|
||||||
|
if isinstance(value, int):
|
||||||
|
return (value,)
|
||||||
|
value = cv.All(cv.ensure_list(cv.int_range(0, 255)), cv.Length(1, 254))(value)
|
||||||
|
return tuple(value)
|
||||||
|
|
||||||
|
|
||||||
|
def power_of_two(value):
|
||||||
|
value = cv.int_range(1, 128)(value)
|
||||||
|
if value & (value - 1) != 0:
|
||||||
|
raise cv.Invalid("value must be a power of two")
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def dimension_schema(rounding):
|
||||||
|
return cv.Any(
|
||||||
|
cv.dimensions,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_WIDTH): validate_dimension(rounding),
|
||||||
|
cv.Required(CONF_HEIGHT): validate_dimension(rounding),
|
||||||
|
cv.Optional(CONF_OFFSET_HEIGHT, default=0): validate_dimension(
|
||||||
|
rounding
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_OFFSET_WIDTH, default=0): validate_dimension(rounding),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def model_schema(bus_mode, model: DriverChip, swapsies: bool):
|
||||||
|
transform = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_MIRROR_X): cv.boolean,
|
||||||
|
cv.Required(CONF_MIRROR_Y): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if model.get_default(CONF_SWAP_XY, False) == cv.UNDEFINED:
|
||||||
|
transform = transform.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_SWAP_XY): cv.invalid(
|
||||||
|
"Axis swapping not supported by this model"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
transform = transform.extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_SWAP_XY): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
# CUSTOM model will need to provide a custom init sequence
|
||||||
|
iseqconf = (
|
||||||
|
cv.Required(CONF_INIT_SEQUENCE)
|
||||||
|
if model.initsequence is None
|
||||||
|
else cv.Optional(CONF_INIT_SEQUENCE)
|
||||||
|
)
|
||||||
|
# Dimensions are optional if the model has a default width and the transform is not overridden
|
||||||
|
cv_dimensions = (
|
||||||
|
cv.Optional if model.get_default(CONF_WIDTH) and not swapsies else cv.Required
|
||||||
|
)
|
||||||
|
pixel_modes = PIXEL_MODES if bus_mode == TYPE_SINGLE else (PIXEL_MODE_16BIT,)
|
||||||
|
color_depth = (
|
||||||
|
("16", "8", "16bit", "8bit") if bus_mode == TYPE_SINGLE else ("16", "16bit")
|
||||||
|
)
|
||||||
|
schema = (
|
||||||
|
display.FULL_DISPLAY_SCHEMA.extend(
|
||||||
|
spi.spi_device_schema(
|
||||||
|
cs_pin_required=False,
|
||||||
|
default_mode="MODE3" if bus_mode == TYPE_OCTAL else "MODE0",
|
||||||
|
default_data_rate=model.get_default(CONF_DATA_RATE, 10_000_000),
|
||||||
|
mode=bus_mode,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
model.option(pin, cv.UNDEFINED): pins.gpio_output_pin_schema
|
||||||
|
for pin in (CONF_RESET_PIN, CONF_CS_PIN, CONF_DC_PIN)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(MipiSpi),
|
||||||
|
cv_dimensions(CONF_DIMENSIONS): dimension_schema(
|
||||||
|
model.get_default(CONF_DRAW_ROUNDING, 1)
|
||||||
|
),
|
||||||
|
model.option(CONF_ENABLE_PIN, cv.UNDEFINED): cv.ensure_list(
|
||||||
|
pins.gpio_output_pin_schema
|
||||||
|
),
|
||||||
|
model.option(CONF_COLOR_ORDER, MODE_BGR): cv.enum(
|
||||||
|
COLOR_ORDERS, upper=True
|
||||||
|
),
|
||||||
|
model.option(CONF_COLOR_DEPTH, 16): cv.one_of(*color_depth, lower=True),
|
||||||
|
model.option(CONF_DRAW_ROUNDING, 2): power_of_two,
|
||||||
|
model.option(CONF_PIXEL_MODE, PIXEL_MODE_16BIT): cv.Any(
|
||||||
|
cv.one_of(*pixel_modes, lower=True),
|
||||||
|
cv.int_range(0, 255, min_included=True, max_included=True),
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TRANSFORM): transform,
|
||||||
|
cv.Optional(CONF_BUS_MODE, default=bus_mode): cv.one_of(
|
||||||
|
bus_mode, lower=True
|
||||||
|
),
|
||||||
|
cv.Required(CONF_MODEL): cv.one_of(model.name, upper=True),
|
||||||
|
iseqconf: cv.ensure_list(map_sequence),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(
|
||||||
|
{
|
||||||
|
model.option(x): cv.boolean
|
||||||
|
for x in [
|
||||||
|
CONF_DRAW_FROM_ORIGIN,
|
||||||
|
CONF_SPI_16,
|
||||||
|
CONF_INVERT_COLORS,
|
||||||
|
CONF_USE_AXIS_FLIPS,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if brightness := model.get_default(CONF_BRIGHTNESS):
|
||||||
|
schema = schema.extend(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_BRIGHTNESS, default=brightness): cv.int_range(
|
||||||
|
0, 0xFF, min_included=True, max_included=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if bus_mode != TYPE_SINGLE:
|
||||||
|
return cv.All(schema, cv.only_with_esp_idf)
|
||||||
|
return schema
|
||||||
|
|
||||||
|
|
||||||
|
def rotation_as_transform(model, config):
|
||||||
|
"""
|
||||||
|
Check if a rotation can be implemented in hardware using the MADCTL register.
|
||||||
|
A rotation of 180 is always possible, 90 and 270 are possible if the model supports swapping X and Y.
|
||||||
|
"""
|
||||||
|
rotation = config.get(CONF_ROTATION, 0)
|
||||||
|
return rotation and (
|
||||||
|
model.get_default(CONF_SWAP_XY) != cv.UNDEFINED or rotation == 180
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def config_schema(config):
|
||||||
|
# First get the model and bus mode
|
||||||
|
config = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True),
|
||||||
|
},
|
||||||
|
extra=ALLOW_EXTRA,
|
||||||
|
)(config)
|
||||||
|
model = MODELS[config[CONF_MODEL]]
|
||||||
|
bus_modes = model.modes
|
||||||
|
config = cv.Schema(
|
||||||
|
{
|
||||||
|
model.option(CONF_BUS_MODE, TYPE_SINGLE): cv.one_of(*bus_modes, lower=True),
|
||||||
|
cv.Required(CONF_MODEL): cv.one_of(*MODELS, upper=True),
|
||||||
|
},
|
||||||
|
extra=ALLOW_EXTRA,
|
||||||
|
)(config)
|
||||||
|
bus_mode = config.get(CONF_BUS_MODE, model.modes[0])
|
||||||
|
swapsies = config.get(CONF_TRANSFORM, {}).get(CONF_SWAP_XY) is True
|
||||||
|
config = model_schema(bus_mode, model, swapsies)(config)
|
||||||
|
# Check for invalid combinations of MADCTL config
|
||||||
|
if init_sequence := config.get(CONF_INIT_SEQUENCE):
|
||||||
|
if MADCTL in [x[0] for x in init_sequence] and CONF_TRANSFORM in config:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"transform is not supported when MADCTL ({MADCTL:#X}) is in the init sequence"
|
||||||
|
)
|
||||||
|
|
||||||
|
if bus_mode == TYPE_QUAD and CONF_DC_PIN in config:
|
||||||
|
raise cv.Invalid("DC pin is not supported in quad mode")
|
||||||
|
if config[CONF_PIXEL_MODE] == PIXEL_MODE_18BIT and bus_mode != TYPE_SINGLE:
|
||||||
|
raise cv.Invalid("18-bit pixel mode is not supported on a quad or octal bus")
|
||||||
|
if bus_mode != TYPE_QUAD and CONF_DC_PIN not in config:
|
||||||
|
raise cv.Invalid(f"DC pin is required in {bus_mode} mode")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = config_schema
|
||||||
|
|
||||||
|
|
||||||
|
def get_transform(model, config):
|
||||||
|
can_transform = rotation_as_transform(model, config)
|
||||||
|
transform = config.get(
|
||||||
|
CONF_TRANSFORM,
|
||||||
|
{
|
||||||
|
CONF_MIRROR_X: model.get_default(CONF_MIRROR_X, False),
|
||||||
|
CONF_MIRROR_Y: model.get_default(CONF_MIRROR_Y, False),
|
||||||
|
CONF_SWAP_XY: model.get_default(CONF_SWAP_XY, False),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Can we use the MADCTL register to set the rotation?
|
||||||
|
if can_transform and CONF_TRANSFORM not in config:
|
||||||
|
rotation = config[CONF_ROTATION]
|
||||||
|
if rotation == 180:
|
||||||
|
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||||
|
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||||
|
elif rotation == 90:
|
||||||
|
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||||
|
transform[CONF_MIRROR_X] = not transform[CONF_MIRROR_X]
|
||||||
|
else:
|
||||||
|
transform[CONF_SWAP_XY] = not transform[CONF_SWAP_XY]
|
||||||
|
transform[CONF_MIRROR_Y] = not transform[CONF_MIRROR_Y]
|
||||||
|
transform[CONF_TRANSFORM] = True
|
||||||
|
return transform
|
||||||
|
|
||||||
|
|
||||||
|
def get_sequence(model, config):
|
||||||
|
"""
|
||||||
|
Create the init sequence for the display.
|
||||||
|
Use the default sequence from the model, if any, and append any custom sequence provided in the config.
|
||||||
|
Append SLPOUT (if not already in the sequence) and DISPON to the end of the sequence
|
||||||
|
Pixel format, color order, and orientation will be set.
|
||||||
|
"""
|
||||||
|
sequence = list(model.initsequence)
|
||||||
|
custom_sequence = config.get(CONF_INIT_SEQUENCE, [])
|
||||||
|
sequence.extend(custom_sequence)
|
||||||
|
# Ensure each command is a tuple
|
||||||
|
sequence = [x if isinstance(x, tuple) else (x,) for x in sequence]
|
||||||
|
commands = [x[0] for x in sequence]
|
||||||
|
# Set pixel format if not already in the custom sequence
|
||||||
|
if PIXFMT not in commands:
|
||||||
|
pixel_mode = config[CONF_PIXEL_MODE]
|
||||||
|
if not isinstance(pixel_mode, int):
|
||||||
|
pixel_mode = PIXEL_MODES[pixel_mode]
|
||||||
|
sequence.append((PIXFMT, pixel_mode))
|
||||||
|
# Does the chip use the flipping bits for mirroring rather than the reverse order bits?
|
||||||
|
use_flip = config[CONF_USE_AXIS_FLIPS]
|
||||||
|
if MADCTL not in commands:
|
||||||
|
madctl = 0
|
||||||
|
transform = get_transform(model, config)
|
||||||
|
if transform.get(CONF_TRANSFORM):
|
||||||
|
LOGGER.info("Using hardware transform to implement rotation")
|
||||||
|
if transform.get(CONF_MIRROR_X):
|
||||||
|
madctl |= MADCTL_XFLIP if use_flip else MADCTL_MX
|
||||||
|
if transform.get(CONF_MIRROR_Y):
|
||||||
|
madctl |= MADCTL_YFLIP if use_flip else MADCTL_MY
|
||||||
|
if transform.get(CONF_SWAP_XY) is True: # Exclude Undefined
|
||||||
|
madctl |= MADCTL_MV
|
||||||
|
if config[CONF_COLOR_ORDER] == MODE_BGR:
|
||||||
|
madctl |= MADCTL_BGR
|
||||||
|
sequence.append((MADCTL, madctl))
|
||||||
|
if INVON not in commands and INVOFF not in commands:
|
||||||
|
if config[CONF_INVERT_COLORS]:
|
||||||
|
sequence.append((INVON,))
|
||||||
|
else:
|
||||||
|
sequence.append((INVOFF,))
|
||||||
|
if BRIGHTNESS not in commands:
|
||||||
|
if brightness := config.get(
|
||||||
|
CONF_BRIGHTNESS, model.get_default(CONF_BRIGHTNESS)
|
||||||
|
):
|
||||||
|
sequence.append((BRIGHTNESS, brightness))
|
||||||
|
if SLPOUT not in commands:
|
||||||
|
sequence.append((SLPOUT,))
|
||||||
|
sequence.append((DISPON,))
|
||||||
|
|
||||||
|
# Flatten the sequence into a list of bytes, with the length of each command
|
||||||
|
# or the delay flag inserted where needed
|
||||||
|
return sum(
|
||||||
|
tuple(
|
||||||
|
(x[1], 0xFF) if x[0] == DELAY_FLAG else (x[0], len(x) - 1) + x[1:]
|
||||||
|
for x in sequence
|
||||||
|
),
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
model = MODELS[config[CONF_MODEL]]
|
||||||
|
transform = get_transform(model, config)
|
||||||
|
if CONF_DIMENSIONS in config:
|
||||||
|
# Explicit dimensions, just use as is
|
||||||
|
dimensions = config[CONF_DIMENSIONS]
|
||||||
|
if isinstance(dimensions, dict):
|
||||||
|
width = dimensions[CONF_WIDTH]
|
||||||
|
height = dimensions[CONF_HEIGHT]
|
||||||
|
offset_width = dimensions[CONF_OFFSET_WIDTH]
|
||||||
|
offset_height = dimensions[CONF_OFFSET_HEIGHT]
|
||||||
|
else:
|
||||||
|
(width, height) = dimensions
|
||||||
|
offset_width = 0
|
||||||
|
offset_height = 0
|
||||||
|
else:
|
||||||
|
# Default dimensions, use model defaults and transform if needed
|
||||||
|
width = model.get_default(CONF_WIDTH)
|
||||||
|
height = model.get_default(CONF_HEIGHT)
|
||||||
|
offset_width = model.get_default(CONF_OFFSET_WIDTH, 0)
|
||||||
|
offset_height = model.get_default(CONF_OFFSET_HEIGHT, 0)
|
||||||
|
|
||||||
|
# if mirroring axes and there are offsets, also mirror the offsets to cater for situations where
|
||||||
|
# the offset is asymmetric
|
||||||
|
if transform[CONF_MIRROR_X]:
|
||||||
|
native_width = model.get_default(
|
||||||
|
CONF_NATIVE_WIDTH, width + offset_width * 2
|
||||||
|
)
|
||||||
|
offset_width = native_width - width - offset_width
|
||||||
|
if transform[CONF_MIRROR_Y]:
|
||||||
|
native_height = model.get_default(
|
||||||
|
CONF_NATIVE_HEIGHT, height + offset_height * 2
|
||||||
|
)
|
||||||
|
offset_height = native_height - height - offset_height
|
||||||
|
# Swap default dimensions if swap_xy is set
|
||||||
|
if transform[CONF_SWAP_XY] is True:
|
||||||
|
width, height = height, width
|
||||||
|
offset_height, offset_width = offset_width, offset_height
|
||||||
|
|
||||||
|
color_depth = config[CONF_COLOR_DEPTH]
|
||||||
|
if color_depth.endswith("bit"):
|
||||||
|
color_depth = color_depth[:-3]
|
||||||
|
color_depth = COLOR_DEPTHS[int(color_depth)]
|
||||||
|
|
||||||
|
var = cg.new_Pvariable(
|
||||||
|
config[CONF_ID], width, height, offset_width, offset_height, color_depth
|
||||||
|
)
|
||||||
|
cg.add(var.set_init_sequence(get_sequence(model, config)))
|
||||||
|
if rotation_as_transform(model, config):
|
||||||
|
if CONF_TRANSFORM in config:
|
||||||
|
LOGGER.warning("Use of 'transform' with 'rotation' is not recommended")
|
||||||
|
else:
|
||||||
|
config[CONF_ROTATION] = 0
|
||||||
|
cg.add(var.set_model(config[CONF_MODEL]))
|
||||||
|
cg.add(var.set_draw_from_origin(config[CONF_DRAW_FROM_ORIGIN]))
|
||||||
|
cg.add(var.set_draw_rounding(config[CONF_DRAW_ROUNDING]))
|
||||||
|
cg.add(var.set_spi_16(config[CONF_SPI_16]))
|
||||||
|
if enable_pin := config.get(CONF_ENABLE_PIN):
|
||||||
|
enable = [await cg.gpio_pin_expression(pin) for pin in enable_pin]
|
||||||
|
cg.add(var.set_enable_pins(enable))
|
||||||
|
|
||||||
|
if reset_pin := config.get(CONF_RESET_PIN):
|
||||||
|
reset = await cg.gpio_pin_expression(reset_pin)
|
||||||
|
cg.add(var.set_reset_pin(reset))
|
||||||
|
|
||||||
|
if dc_pin := config.get(CONF_DC_PIN):
|
||||||
|
dc_pin = await cg.gpio_pin_expression(dc_pin)
|
||||||
|
cg.add(var.set_dc_pin(dc_pin))
|
||||||
|
|
||||||
|
if lamb := config.get(CONF_LAMBDA):
|
||||||
|
lambda_ = await cg.process_lambda(
|
||||||
|
lamb, [(display.DisplayRef, "it")], return_type=cg.void
|
||||||
|
)
|
||||||
|
cg.add(var.set_writer(lambda_))
|
||||||
|
await display.register_display(var, config)
|
||||||
|
await spi.register_spi_device(var, config)
|
481
esphome/components/mipi_spi/mipi_spi.cpp
Normal file
481
esphome/components/mipi_spi/mipi_spi.cpp
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
#include "mipi_spi.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mipi_spi {
|
||||||
|
|
||||||
|
void MipiSpi::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up MIPI SPI");
|
||||||
|
this->spi_setup();
|
||||||
|
if (this->dc_pin_ != nullptr) {
|
||||||
|
this->dc_pin_->setup();
|
||||||
|
this->dc_pin_->digital_write(false);
|
||||||
|
}
|
||||||
|
for (auto *pin : this->enable_pins_) {
|
||||||
|
pin->setup();
|
||||||
|
pin->digital_write(true);
|
||||||
|
}
|
||||||
|
if (this->reset_pin_ != nullptr) {
|
||||||
|
this->reset_pin_->setup();
|
||||||
|
this->reset_pin_->digital_write(true);
|
||||||
|
delay(5);
|
||||||
|
this->reset_pin_->digital_write(false);
|
||||||
|
delay(5);
|
||||||
|
this->reset_pin_->digital_write(true);
|
||||||
|
}
|
||||||
|
this->bus_width_ = this->parent_->get_bus_width();
|
||||||
|
|
||||||
|
// need to know when the display is ready for SLPOUT command - will be 120ms after reset
|
||||||
|
auto when = millis() + 120;
|
||||||
|
delay(10);
|
||||||
|
size_t index = 0;
|
||||||
|
auto &vec = this->init_sequence_;
|
||||||
|
while (index != vec.size()) {
|
||||||
|
if (vec.size() - index < 2) {
|
||||||
|
ESP_LOGE(TAG, "Malformed init sequence");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t cmd = vec[index++];
|
||||||
|
uint8_t x = vec[index++];
|
||||||
|
if (x == DELAY_FLAG) {
|
||||||
|
ESP_LOGD(TAG, "Delay %dms", cmd);
|
||||||
|
delay(cmd);
|
||||||
|
} else {
|
||||||
|
uint8_t num_args = x & 0x7F;
|
||||||
|
if (vec.size() - index < num_args) {
|
||||||
|
ESP_LOGE(TAG, "Malformed init sequence");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto arg_byte = vec[index];
|
||||||
|
switch (cmd) {
|
||||||
|
case SLEEP_OUT: {
|
||||||
|
// are we ready, boots?
|
||||||
|
int duration = when - millis();
|
||||||
|
if (duration > 0) {
|
||||||
|
ESP_LOGD(TAG, "Sleep %dms", duration);
|
||||||
|
delay(duration);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case INVERT_ON:
|
||||||
|
this->invert_colors_ = true;
|
||||||
|
break;
|
||||||
|
case MADCTL_CMD:
|
||||||
|
this->madctl_ = arg_byte;
|
||||||
|
break;
|
||||||
|
case PIXFMT:
|
||||||
|
this->pixel_mode_ = arg_byte & 0x11 ? PIXEL_MODE_16 : PIXEL_MODE_18;
|
||||||
|
break;
|
||||||
|
case BRIGHTNESS:
|
||||||
|
this->brightness_ = arg_byte;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const auto *ptr = vec.data() + index;
|
||||||
|
ESP_LOGD(TAG, "Command %02X, length %d, byte %02X", cmd, num_args, arg_byte);
|
||||||
|
this->write_command_(cmd, ptr, num_args);
|
||||||
|
index += num_args;
|
||||||
|
if (cmd == SLEEP_OUT)
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->setup_complete_ = true;
|
||||||
|
if (this->draw_from_origin_)
|
||||||
|
check_buffer_();
|
||||||
|
ESP_LOGCONFIG(TAG, "MIPI SPI setup complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::update() {
|
||||||
|
if (!this->setup_complete_ || this->is_failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->do_update_();
|
||||||
|
if (this->buffer_ == nullptr || this->x_low_ > this->x_high_ || this->y_low_ > this->y_high_)
|
||||||
|
return;
|
||||||
|
ESP_LOGV(TAG, "x_low %d, y_low %d, x_high %d, y_high %d", this->x_low_, this->y_low_, this->x_high_, this->y_high_);
|
||||||
|
// Some chips require that the drawing window be aligned on certain boundaries
|
||||||
|
auto dr = this->draw_rounding_;
|
||||||
|
this->x_low_ = this->x_low_ / dr * dr;
|
||||||
|
this->y_low_ = this->y_low_ / dr * dr;
|
||||||
|
this->x_high_ = (this->x_high_ + dr) / dr * dr - 1;
|
||||||
|
this->y_high_ = (this->y_high_ + dr) / dr * dr - 1;
|
||||||
|
if (this->draw_from_origin_) {
|
||||||
|
this->x_low_ = 0;
|
||||||
|
this->y_low_ = 0;
|
||||||
|
this->x_high_ = this->width_ - 1;
|
||||||
|
}
|
||||||
|
int w = this->x_high_ - this->x_low_ + 1;
|
||||||
|
int h = this->y_high_ - this->y_low_ + 1;
|
||||||
|
this->write_to_display_(this->x_low_, this->y_low_, w, h, this->buffer_, this->x_low_, this->y_low_,
|
||||||
|
this->width_ - w - this->x_low_);
|
||||||
|
// invalidate watermarks
|
||||||
|
this->x_low_ = this->width_;
|
||||||
|
this->y_low_ = this->height_;
|
||||||
|
this->x_high_ = 0;
|
||||||
|
this->y_high_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::fill(Color color) {
|
||||||
|
if (!this->check_buffer_())
|
||||||
|
return;
|
||||||
|
this->x_low_ = 0;
|
||||||
|
this->y_low_ = 0;
|
||||||
|
this->x_high_ = this->get_width_internal() - 1;
|
||||||
|
this->y_high_ = this->get_height_internal() - 1;
|
||||||
|
switch (this->color_depth_) {
|
||||||
|
case display::COLOR_BITNESS_332: {
|
||||||
|
auto new_color = display::ColorUtil::color_to_332(color, display::ColorOrder::COLOR_ORDER_RGB);
|
||||||
|
memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
auto new_color = display::ColorUtil::color_to_565(color);
|
||||||
|
if (((uint8_t) (new_color >> 8)) == ((uint8_t) new_color)) {
|
||||||
|
// Upper and lower is equal can use quicker memset operation. Takes ~20ms.
|
||||||
|
memset(this->buffer_, (uint8_t) new_color, this->buffer_bytes_);
|
||||||
|
} else {
|
||||||
|
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
|
||||||
|
auto len = this->buffer_bytes_ / 2;
|
||||||
|
while (len--) {
|
||||||
|
*ptr_16++ = new_color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||||
|
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->check_buffer_())
|
||||||
|
return;
|
||||||
|
size_t pos = (y * this->width_) + x;
|
||||||
|
switch (this->color_depth_) {
|
||||||
|
case display::COLOR_BITNESS_332: {
|
||||||
|
uint8_t new_color = display::ColorUtil::color_to_332(color);
|
||||||
|
if (this->buffer_[pos] == new_color)
|
||||||
|
return;
|
||||||
|
this->buffer_[pos] = new_color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case display::COLOR_BITNESS_565: {
|
||||||
|
auto *ptr_16 = reinterpret_cast<uint16_t *>(this->buffer_);
|
||||||
|
uint8_t hi_byte = static_cast<uint8_t>(color.r & 0xF8) | (color.g >> 5);
|
||||||
|
uint8_t lo_byte = static_cast<uint8_t>((color.g & 0x1C) << 3) | (color.b >> 3);
|
||||||
|
uint16_t new_color = hi_byte | (lo_byte << 8); // big endian
|
||||||
|
if (ptr_16[pos] == new_color)
|
||||||
|
return;
|
||||||
|
ptr_16[pos] = new_color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// low and high watermark may speed up drawing from buffer
|
||||||
|
if (x < this->x_low_)
|
||||||
|
this->x_low_ = x;
|
||||||
|
if (y < this->y_low_)
|
||||||
|
this->y_low_ = y;
|
||||||
|
if (x > this->x_high_)
|
||||||
|
this->x_high_ = x;
|
||||||
|
if (y > this->y_high_)
|
||||||
|
this->y_high_ = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::reset_params_() {
|
||||||
|
if (!this->is_ready())
|
||||||
|
return;
|
||||||
|
this->write_command_(this->invert_colors_ ? INVERT_ON : INVERT_OFF);
|
||||||
|
if (this->brightness_.has_value())
|
||||||
|
this->write_command_(BRIGHTNESS, this->brightness_.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_init_sequence_() {
|
||||||
|
size_t index = 0;
|
||||||
|
auto &vec = this->init_sequence_;
|
||||||
|
while (index != vec.size()) {
|
||||||
|
if (vec.size() - index < 2) {
|
||||||
|
ESP_LOGE(TAG, "Malformed init sequence");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t cmd = vec[index++];
|
||||||
|
uint8_t x = vec[index++];
|
||||||
|
if (x == DELAY_FLAG) {
|
||||||
|
ESP_LOGV(TAG, "Delay %dms", cmd);
|
||||||
|
delay(cmd);
|
||||||
|
} else {
|
||||||
|
uint8_t num_args = x & 0x7F;
|
||||||
|
if (vec.size() - index < num_args) {
|
||||||
|
ESP_LOGE(TAG, "Malformed init sequence");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto *ptr = vec.data() + index;
|
||||||
|
this->write_command_(cmd, ptr, num_args);
|
||||||
|
index += num_args;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->setup_complete_ = true;
|
||||||
|
ESP_LOGCONFIG(TAG, "MIPI SPI setup complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
|
||||||
|
ESP_LOGVV(TAG, "Set addr %d/%d, %d/%d", x1, y1, x2, y2);
|
||||||
|
uint8_t buf[4];
|
||||||
|
x1 += this->offset_width_;
|
||||||
|
x2 += this->offset_width_;
|
||||||
|
y1 += this->offset_height_;
|
||||||
|
y2 += this->offset_height_;
|
||||||
|
put16_be(buf, y1);
|
||||||
|
put16_be(buf + 2, y2);
|
||||||
|
this->write_command_(RASET, buf, sizeof buf);
|
||||||
|
put16_be(buf, x1);
|
||||||
|
put16_be(buf + 2, x2);
|
||||||
|
this->write_command_(CASET, buf, sizeof buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
|
||||||
|
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) {
|
||||||
|
if (!this->setup_complete_ || this->is_failed())
|
||||||
|
return;
|
||||||
|
if (w <= 0 || h <= 0)
|
||||||
|
return;
|
||||||
|
if (bitness != this->color_depth_ || big_endian != (this->bit_order_ == spi::BIT_ORDER_MSB_FIRST)) {
|
||||||
|
Display::draw_pixels_at(x_start, y_start, w, h, ptr, order, bitness, big_endian, x_offset, y_offset, x_pad);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->draw_from_origin_) {
|
||||||
|
auto stride = x_offset + w + x_pad;
|
||||||
|
for (int y = 0; y != h; y++) {
|
||||||
|
memcpy(this->buffer_ + ((y + y_start) * this->width_ + x_start) * 2,
|
||||||
|
ptr + ((y + y_offset) * stride + x_offset) * 2, w * 2);
|
||||||
|
}
|
||||||
|
ptr = this->buffer_;
|
||||||
|
w = this->width_;
|
||||||
|
h += y_start;
|
||||||
|
x_start = 0;
|
||||||
|
y_start = 0;
|
||||||
|
x_offset = 0;
|
||||||
|
y_offset = 0;
|
||||||
|
}
|
||||||
|
this->write_to_display_(x_start, y_start, w, h, ptr, x_offset, y_offset, x_pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride) {
|
||||||
|
stride -= w;
|
||||||
|
uint8_t transfer_buffer[6 * 256];
|
||||||
|
size_t idx = 0; // index into transfer_buffer
|
||||||
|
while (h-- != 0) {
|
||||||
|
for (auto x = w; x-- != 0;) {
|
||||||
|
auto color_val = *ptr++;
|
||||||
|
// deal with byte swapping
|
||||||
|
transfer_buffer[idx++] = (color_val & 0xF8); // Blue
|
||||||
|
transfer_buffer[idx++] = ((color_val & 0x7) << 5) | ((color_val & 0xE000) >> 11); // Green
|
||||||
|
transfer_buffer[idx++] = (color_val >> 5) & 0xF8; // Red
|
||||||
|
if (idx == sizeof(transfer_buffer)) {
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
if (idx != 0)
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) {
|
||||||
|
stride -= w;
|
||||||
|
uint8_t transfer_buffer[6 * 256];
|
||||||
|
size_t idx = 0; // index into transfer_buffer
|
||||||
|
while (h-- != 0) {
|
||||||
|
for (auto x = w; x-- != 0;) {
|
||||||
|
auto color_val = *ptr++;
|
||||||
|
transfer_buffer[idx++] = color_val & 0xE0; // Red
|
||||||
|
transfer_buffer[idx++] = (color_val << 3) & 0xE0; // Green
|
||||||
|
transfer_buffer[idx++] = color_val << 6; // Blue
|
||||||
|
if (idx == sizeof(transfer_buffer)) {
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
if (idx != 0)
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride) {
|
||||||
|
stride -= w;
|
||||||
|
uint8_t transfer_buffer[6 * 256];
|
||||||
|
size_t idx = 0; // index into transfer_buffer
|
||||||
|
while (h-- != 0) {
|
||||||
|
for (auto x = w; x-- != 0;) {
|
||||||
|
auto color_val = *ptr++;
|
||||||
|
transfer_buffer[idx++] = (color_val & 0xE0) | ((color_val & 0x1C) >> 2);
|
||||||
|
transfer_buffer[idx++] = (color_val & 0x3) << 3;
|
||||||
|
if (idx == sizeof(transfer_buffer)) {
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ptr += stride;
|
||||||
|
}
|
||||||
|
if (idx != 0)
|
||||||
|
this->write_array(transfer_buffer, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
|
||||||
|
int x_pad) {
|
||||||
|
this->set_addr_window_(x_start, y_start, x_start + w - 1, y_start + h - 1);
|
||||||
|
auto stride = x_offset + w + x_pad;
|
||||||
|
const auto *offset_ptr = ptr;
|
||||||
|
if (this->color_depth_ == display::COLOR_BITNESS_332) {
|
||||||
|
offset_ptr += y_offset * stride + x_offset;
|
||||||
|
} else {
|
||||||
|
stride *= 2;
|
||||||
|
offset_ptr += y_offset * stride + x_offset * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (this->bus_width_) {
|
||||||
|
case 4:
|
||||||
|
this->enable();
|
||||||
|
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
|
||||||
|
// we could deal here with a non-zero y_offset, but if x_offset is zero, y_offset probably will be so don't
|
||||||
|
// bother
|
||||||
|
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, ptr, w * h * 2, 4);
|
||||||
|
} else {
|
||||||
|
this->write_cmd_addr_data(8, 0x32, 24, WDATA << 8, nullptr, 0, 4);
|
||||||
|
for (int y = 0; y != h; y++) {
|
||||||
|
this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 4);
|
||||||
|
offset_ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
this->write_command_(WDATA);
|
||||||
|
this->enable();
|
||||||
|
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
|
||||||
|
this->write_cmd_addr_data(0, 0, 0, 0, ptr, w * h * 2, 8);
|
||||||
|
} else {
|
||||||
|
for (int y = 0; y != h; y++) {
|
||||||
|
this->write_cmd_addr_data(0, 0, 0, 0, offset_ptr, w * 2, 8);
|
||||||
|
offset_ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this->write_command_(WDATA);
|
||||||
|
this->enable();
|
||||||
|
|
||||||
|
if (this->color_depth_ == display::COLOR_BITNESS_565) {
|
||||||
|
// Source buffer is 16-bit RGB565
|
||||||
|
if (this->pixel_mode_ == PIXEL_MODE_18) {
|
||||||
|
// Convert RGB565 to RGB666
|
||||||
|
this->write_18_from_16_bit_(reinterpret_cast<const uint16_t *>(offset_ptr), w, h, stride / 2);
|
||||||
|
} else {
|
||||||
|
// Direct RGB565 output
|
||||||
|
if (x_offset == 0 && x_pad == 0 && y_offset == 0) {
|
||||||
|
this->write_array(ptr, w * h * 2);
|
||||||
|
} else {
|
||||||
|
for (int y = 0; y != h; y++) {
|
||||||
|
this->write_array(offset_ptr, w * 2);
|
||||||
|
offset_ptr += stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Source buffer is 8-bit RGB332
|
||||||
|
if (this->pixel_mode_ == PIXEL_MODE_18) {
|
||||||
|
// Convert RGB332 to RGB666
|
||||||
|
this->write_18_from_8_bit_(offset_ptr, w, h, stride);
|
||||||
|
} else {
|
||||||
|
this->write_16_from_8_bit_(offset_ptr, w, h, stride);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::write_command_(uint8_t cmd, const uint8_t *bytes, size_t len) {
|
||||||
|
ESP_LOGV(TAG, "Command %02X, length %d, bytes %s", cmd, len, format_hex_pretty(bytes, len).c_str());
|
||||||
|
if (this->bus_width_ == 4) {
|
||||||
|
this->enable();
|
||||||
|
this->write_cmd_addr_data(8, 0x02, 24, cmd << 8, bytes, len);
|
||||||
|
this->disable();
|
||||||
|
} else if (this->bus_width_ == 8) {
|
||||||
|
this->dc_pin_->digital_write(false);
|
||||||
|
this->enable();
|
||||||
|
this->write_cmd_addr_data(0, 0, 0, 0, &cmd, 1, 8);
|
||||||
|
this->disable();
|
||||||
|
this->dc_pin_->digital_write(true);
|
||||||
|
if (len != 0) {
|
||||||
|
this->enable();
|
||||||
|
this->write_cmd_addr_data(0, 0, 0, 0, bytes, len, 8);
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->dc_pin_->digital_write(false);
|
||||||
|
this->enable();
|
||||||
|
this->write_byte(cmd);
|
||||||
|
this->disable();
|
||||||
|
this->dc_pin_->digital_write(true);
|
||||||
|
if (len != 0) {
|
||||||
|
if (this->spi_16_) {
|
||||||
|
for (size_t i = 0; i != len; i++) {
|
||||||
|
this->enable();
|
||||||
|
this->write_byte(0);
|
||||||
|
this->write_byte(bytes[i]);
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->enable();
|
||||||
|
this->write_array(bytes, len);
|
||||||
|
this->disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MipiSpi::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "MIPI_SPI Display");
|
||||||
|
ESP_LOGCONFIG(TAG, " Model: %s", this->model_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Width: %u", this->width_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Height: %u", this->height_);
|
||||||
|
if (this->offset_width_ != 0)
|
||||||
|
ESP_LOGCONFIG(TAG, " Offset width: %u", this->offset_width_);
|
||||||
|
if (this->offset_height_ != 0)
|
||||||
|
ESP_LOGCONFIG(TAG, " Offset height: %u", this->offset_height_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Swap X/Y: %s", YESNO(this->madctl_ & MADCTL_MV));
|
||||||
|
ESP_LOGCONFIG(TAG, " Mirror X: %s", YESNO(this->madctl_ & (MADCTL_MX | MADCTL_XFLIP)));
|
||||||
|
ESP_LOGCONFIG(TAG, " Mirror Y: %s", YESNO(this->madctl_ & (MADCTL_MY | MADCTL_YFLIP)));
|
||||||
|
ESP_LOGCONFIG(TAG, " Color depth: %d bits", this->color_depth_ == display::COLOR_BITNESS_565 ? 16 : 8);
|
||||||
|
ESP_LOGCONFIG(TAG, " Invert colors: %s", YESNO(this->invert_colors_));
|
||||||
|
ESP_LOGCONFIG(TAG, " Color order: %s", this->madctl_ & MADCTL_BGR ? "BGR" : "RGB");
|
||||||
|
ESP_LOGCONFIG(TAG, " Pixel mode: %s", this->pixel_mode_ == PIXEL_MODE_18 ? "18bit" : "16bit");
|
||||||
|
if (this->brightness_.has_value())
|
||||||
|
ESP_LOGCONFIG(TAG, " Brightness: %u", this->brightness_.value());
|
||||||
|
if (this->spi_16_)
|
||||||
|
ESP_LOGCONFIG(TAG, " SPI 16bit: YES");
|
||||||
|
ESP_LOGCONFIG(TAG, " Draw rounding: %u", this->draw_rounding_);
|
||||||
|
if (this->draw_from_origin_)
|
||||||
|
ESP_LOGCONFIG(TAG, " Draw from origin: YES");
|
||||||
|
LOG_PIN(" CS Pin: ", this->cs_);
|
||||||
|
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||||
|
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||||
|
ESP_LOGCONFIG(TAG, " SPI Mode: %d", this->mode_);
|
||||||
|
ESP_LOGCONFIG(TAG, " SPI Data rate: %dMHz", static_cast<unsigned>(this->data_rate_ / 1000000));
|
||||||
|
ESP_LOGCONFIG(TAG, " SPI Bus width: %d", this->bus_width_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mipi_spi
|
||||||
|
} // namespace esphome
|
171
esphome/components/mipi_spi/mipi_spi.h
Normal file
171
esphome/components/mipi_spi/mipi_spi.h
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "esphome/components/spi/spi.h"
|
||||||
|
#include "esphome/components/display/display.h"
|
||||||
|
#include "esphome/components/display/display_buffer.h"
|
||||||
|
#include "esphome/components/display/display_color_utils.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace mipi_spi {
|
||||||
|
|
||||||
|
constexpr static const char *const TAG = "display.mipi_spi";
|
||||||
|
static const uint8_t SW_RESET_CMD = 0x01;
|
||||||
|
static const uint8_t SLEEP_OUT = 0x11;
|
||||||
|
static const uint8_t NORON = 0x13;
|
||||||
|
static const uint8_t INVERT_OFF = 0x20;
|
||||||
|
static const uint8_t INVERT_ON = 0x21;
|
||||||
|
static const uint8_t ALL_ON = 0x23;
|
||||||
|
static const uint8_t WRAM = 0x24;
|
||||||
|
static const uint8_t MIPI = 0x26;
|
||||||
|
static const uint8_t DISPLAY_ON = 0x29;
|
||||||
|
static const uint8_t RASET = 0x2B;
|
||||||
|
static const uint8_t CASET = 0x2A;
|
||||||
|
static const uint8_t WDATA = 0x2C;
|
||||||
|
static const uint8_t TEON = 0x35;
|
||||||
|
static const uint8_t MADCTL_CMD = 0x36;
|
||||||
|
static const uint8_t PIXFMT = 0x3A;
|
||||||
|
static const uint8_t BRIGHTNESS = 0x51;
|
||||||
|
static const uint8_t SWIRE1 = 0x5A;
|
||||||
|
static const uint8_t SWIRE2 = 0x5B;
|
||||||
|
static const uint8_t PAGESEL = 0xFE;
|
||||||
|
|
||||||
|
static const uint8_t MADCTL_MY = 0x80; // Bit 7 Bottom to top
|
||||||
|
static const uint8_t MADCTL_MX = 0x40; // Bit 6 Right to left
|
||||||
|
static const uint8_t MADCTL_MV = 0x20; // Bit 5 Swap axes
|
||||||
|
static const uint8_t MADCTL_RGB = 0x00; // Bit 3 Red-Green-Blue pixel order
|
||||||
|
static const uint8_t MADCTL_BGR = 0x08; // Bit 3 Blue-Green-Red pixel order
|
||||||
|
static const uint8_t MADCTL_XFLIP = 0x02; // Mirror the display horizontally
|
||||||
|
static const uint8_t MADCTL_YFLIP = 0x01; // Mirror the display vertically
|
||||||
|
|
||||||
|
static const uint8_t DELAY_FLAG = 0xFF;
|
||||||
|
// store a 16 bit value in a buffer, big endian.
|
||||||
|
static inline void put16_be(uint8_t *buf, uint16_t value) {
|
||||||
|
buf[0] = value >> 8;
|
||||||
|
buf[1] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PixelMode {
|
||||||
|
PIXEL_MODE_16,
|
||||||
|
PIXEL_MODE_18,
|
||||||
|
};
|
||||||
|
|
||||||
|
class MipiSpi : public display::DisplayBuffer,
|
||||||
|
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||||
|
spi::DATA_RATE_1MHZ> {
|
||||||
|
public:
|
||||||
|
MipiSpi(size_t width, size_t height, int16_t offset_width, int16_t offset_height, display::ColorBitness color_depth)
|
||||||
|
: width_(width),
|
||||||
|
height_(height),
|
||||||
|
offset_width_(offset_width),
|
||||||
|
offset_height_(offset_height),
|
||||||
|
color_depth_(color_depth) {}
|
||||||
|
void set_model(const char *model) { this->model_ = model; }
|
||||||
|
void update() override;
|
||||||
|
void setup() override;
|
||||||
|
display::ColorOrder get_color_mode() {
|
||||||
|
return this->madctl_ & MADCTL_BGR ? display::COLOR_ORDER_BGR : display::COLOR_ORDER_RGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
|
||||||
|
void set_enable_pins(std::vector<GPIOPin *> enable_pins) { this->enable_pins_ = std::move(enable_pins); }
|
||||||
|
void set_dc_pin(GPIOPin *dc_pin) { this->dc_pin_ = dc_pin; }
|
||||||
|
void set_invert_colors(bool invert_colors) {
|
||||||
|
this->invert_colors_ = invert_colors;
|
||||||
|
this->reset_params_();
|
||||||
|
}
|
||||||
|
void set_brightness(uint8_t brightness) {
|
||||||
|
this->brightness_ = brightness;
|
||||||
|
this->reset_params_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_draw_from_origin(bool draw_from_origin) { this->draw_from_origin_ = draw_from_origin; }
|
||||||
|
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
int get_width_internal() override { return this->width_; }
|
||||||
|
int get_height_internal() override { return this->height_; }
|
||||||
|
bool can_proceed() override { return this->setup_complete_; }
|
||||||
|
void set_init_sequence(const std::vector<uint8_t> &sequence) { this->init_sequence_ = sequence; }
|
||||||
|
void set_draw_rounding(unsigned rounding) { this->draw_rounding_ = rounding; }
|
||||||
|
void set_spi_16(bool spi_16) { this->spi_16_ = spi_16; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool check_buffer_() {
|
||||||
|
if (this->is_failed())
|
||||||
|
return false;
|
||||||
|
if (this->buffer_ != nullptr)
|
||||||
|
return true;
|
||||||
|
auto bytes_per_pixel = this->color_depth_ == display::COLOR_BITNESS_565 ? 2 : 1;
|
||||||
|
this->init_internal_(this->width_ * this->height_ * bytes_per_pixel);
|
||||||
|
if (this->buffer_ == nullptr) {
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this->buffer_bytes_ = this->width_ * this->height_ * bytes_per_pixel;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void fill(Color color) override;
|
||||||
|
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||||
|
void draw_pixels_at(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorOrder order,
|
||||||
|
display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override;
|
||||||
|
void write_18_from_16_bit_(const uint16_t *ptr, size_t w, size_t h, size_t stride);
|
||||||
|
void write_18_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride);
|
||||||
|
void write_16_from_8_bit_(const uint8_t *ptr, size_t w, size_t h, size_t stride);
|
||||||
|
void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset,
|
||||||
|
int x_pad);
|
||||||
|
/**
|
||||||
|
* the RM67162 in quad SPI mode seems to work like this (not in the datasheet, this is deduced from the
|
||||||
|
* sample code.)
|
||||||
|
*
|
||||||
|
* Immediately after enabling /CS send 4 bytes in single-dataline SPI mode:
|
||||||
|
* 0: either 0x2 or 0x32. The first indicates that any subsequent data bytes after the initial 4 will be
|
||||||
|
* sent in 1-dataline SPI. The second indicates quad mode.
|
||||||
|
* 1: 0x00
|
||||||
|
* 2: The command (register address) byte.
|
||||||
|
* 3: 0x00
|
||||||
|
*
|
||||||
|
* This is followed by zero or more data bytes in either 1-wire or 4-wire mode, depending on the first byte.
|
||||||
|
* At the conclusion of the write, de-assert /CS.
|
||||||
|
*
|
||||||
|
* @param cmd
|
||||||
|
* @param bytes
|
||||||
|
* @param len
|
||||||
|
*/
|
||||||
|
void write_command_(uint8_t cmd, const uint8_t *bytes, size_t len);
|
||||||
|
|
||||||
|
void write_command_(uint8_t cmd, uint8_t data) { this->write_command_(cmd, &data, 1); }
|
||||||
|
void write_command_(uint8_t cmd) { this->write_command_(cmd, &cmd, 0); }
|
||||||
|
void reset_params_();
|
||||||
|
void write_init_sequence_();
|
||||||
|
void set_addr_window_(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
|
||||||
|
|
||||||
|
GPIOPin *reset_pin_{nullptr};
|
||||||
|
std::vector<GPIOPin *> enable_pins_{};
|
||||||
|
GPIOPin *dc_pin_{nullptr};
|
||||||
|
uint16_t x_low_{1};
|
||||||
|
uint16_t y_low_{1};
|
||||||
|
uint16_t x_high_{0};
|
||||||
|
uint16_t y_high_{0};
|
||||||
|
bool setup_complete_{};
|
||||||
|
|
||||||
|
bool invert_colors_{};
|
||||||
|
size_t width_;
|
||||||
|
size_t height_;
|
||||||
|
int16_t offset_width_;
|
||||||
|
int16_t offset_height_;
|
||||||
|
size_t buffer_bytes_{0};
|
||||||
|
display::ColorBitness color_depth_;
|
||||||
|
PixelMode pixel_mode_{PIXEL_MODE_16};
|
||||||
|
uint8_t bus_width_{};
|
||||||
|
bool spi_16_{};
|
||||||
|
uint8_t madctl_{};
|
||||||
|
bool draw_from_origin_{false};
|
||||||
|
unsigned draw_rounding_{2};
|
||||||
|
optional<uint8_t> brightness_{};
|
||||||
|
const char *model_{"Unknown"};
|
||||||
|
std::vector<uint8_t> init_sequence_{};
|
||||||
|
};
|
||||||
|
} // namespace mipi_spi
|
||||||
|
} // namespace esphome
|
65
esphome/components/mipi_spi/models/__init__.py
Normal file
65
esphome/components/mipi_spi/models/__init__.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
from esphome.components.spi import TYPE_OCTAL, TYPE_QUAD, TYPE_SINGLE
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_HEIGHT, CONF_OFFSET_HEIGHT, CONF_OFFSET_WIDTH, CONF_WIDTH
|
||||||
|
|
||||||
|
from .. import CONF_NATIVE_HEIGHT, CONF_NATIVE_WIDTH
|
||||||
|
|
||||||
|
MADCTL_MY = 0x80 # Bit 7 Bottom to top
|
||||||
|
MADCTL_MX = 0x40 # Bit 6 Right to left
|
||||||
|
MADCTL_MV = 0x20 # Bit 5 Reverse Mode
|
||||||
|
MADCTL_ML = 0x10 # Bit 4 LCD refresh Bottom to top
|
||||||
|
MADCTL_RGB = 0x00 # Bit 3 Red-Green-Blue pixel order
|
||||||
|
MADCTL_BGR = 0x08 # Bit 3 Blue-Green-Red pixel order
|
||||||
|
MADCTL_MH = 0x04 # Bit 2 LCD refresh right to left
|
||||||
|
|
||||||
|
# These bits are used instead of the above bits on some chips, where using MX and MY results in incorrect
|
||||||
|
# partial updates.
|
||||||
|
MADCTL_XFLIP = 0x02 # Mirror the display horizontally
|
||||||
|
MADCTL_YFLIP = 0x01 # Mirror the display vertically
|
||||||
|
|
||||||
|
DELAY_FLAG = 0xFFF # Special flag to indicate a delay
|
||||||
|
|
||||||
|
|
||||||
|
def delay(ms):
|
||||||
|
return DELAY_FLAG, ms
|
||||||
|
|
||||||
|
|
||||||
|
class DriverChip:
|
||||||
|
models = {}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
modes=(TYPE_SINGLE, TYPE_QUAD, TYPE_OCTAL),
|
||||||
|
initsequence=None,
|
||||||
|
**defaults,
|
||||||
|
):
|
||||||
|
name = name.upper()
|
||||||
|
self.name = name
|
||||||
|
self.modes = modes
|
||||||
|
self.initsequence = initsequence
|
||||||
|
self.defaults = defaults
|
||||||
|
DriverChip.models[name] = self
|
||||||
|
|
||||||
|
def extend(self, name, **kwargs):
|
||||||
|
defaults = self.defaults.copy()
|
||||||
|
if (
|
||||||
|
CONF_WIDTH in defaults
|
||||||
|
and CONF_OFFSET_WIDTH in kwargs
|
||||||
|
and CONF_NATIVE_WIDTH not in defaults
|
||||||
|
):
|
||||||
|
defaults[CONF_NATIVE_WIDTH] = defaults[CONF_WIDTH]
|
||||||
|
if (
|
||||||
|
CONF_HEIGHT in defaults
|
||||||
|
and CONF_OFFSET_HEIGHT in kwargs
|
||||||
|
and CONF_NATIVE_HEIGHT not in defaults
|
||||||
|
):
|
||||||
|
defaults[CONF_NATIVE_HEIGHT] = defaults[CONF_HEIGHT]
|
||||||
|
defaults.update(kwargs)
|
||||||
|
return DriverChip(name, self.modes, initsequence=self.initsequence, **defaults)
|
||||||
|
|
||||||
|
def get_default(self, key, fallback=False):
|
||||||
|
return self.defaults.get(key, fallback)
|
||||||
|
|
||||||
|
def option(self, name, fallback=False):
|
||||||
|
return cv.Optional(name, default=self.get_default(name, fallback))
|
72
esphome/components/mipi_spi/models/amoled.py
Normal file
72
esphome/components/mipi_spi/models/amoled.py
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
from esphome.components.spi import TYPE_QUAD
|
||||||
|
|
||||||
|
from .. import MODE_RGB
|
||||||
|
from . import DriverChip, delay
|
||||||
|
from .commands import MIPI, NORON, PAGESEL, PIXFMT, SLPOUT, SWIRE1, SWIRE2, TEON, WRAM
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
"T-DISPLAY-S3-AMOLED",
|
||||||
|
width=240,
|
||||||
|
height=536,
|
||||||
|
cs_pin=6,
|
||||||
|
reset_pin=17,
|
||||||
|
enable_pin=38,
|
||||||
|
bus_mode=TYPE_QUAD,
|
||||||
|
brightness=0xD0,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
initsequence=(SLPOUT,), # Requires early SLPOUT
|
||||||
|
)
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
name="T-DISPLAY-S3-AMOLED-PLUS",
|
||||||
|
width=240,
|
||||||
|
height=536,
|
||||||
|
cs_pin=6,
|
||||||
|
reset_pin=17,
|
||||||
|
dc_pin=7,
|
||||||
|
enable_pin=38,
|
||||||
|
data_rate="40MHz",
|
||||||
|
brightness=0xD0,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
initsequence=(
|
||||||
|
(PAGESEL, 4),
|
||||||
|
(0x6A, 0x00),
|
||||||
|
(PAGESEL, 0x05),
|
||||||
|
(PAGESEL, 0x07),
|
||||||
|
(0x07, 0x4F),
|
||||||
|
(PAGESEL, 0x01),
|
||||||
|
(0x2A, 0x02),
|
||||||
|
(0x2B, 0x73),
|
||||||
|
(PAGESEL, 0x0A),
|
||||||
|
(0x29, 0x10),
|
||||||
|
(PAGESEL, 0x00),
|
||||||
|
(0x53, 0x20),
|
||||||
|
(TEON, 0x00),
|
||||||
|
(PIXFMT, 0x75),
|
||||||
|
(0xC4, 0x80),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
RM690B0 = DriverChip(
|
||||||
|
"RM690B0",
|
||||||
|
brightness=0xD0,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
width=480,
|
||||||
|
height=600,
|
||||||
|
initsequence=(
|
||||||
|
(PAGESEL, 0x20),
|
||||||
|
(MIPI, 0x0A),
|
||||||
|
(WRAM, 0x80),
|
||||||
|
(SWIRE1, 0x51),
|
||||||
|
(SWIRE2, 0x2E),
|
||||||
|
(PAGESEL, 0x00),
|
||||||
|
(0xC2, 0x00),
|
||||||
|
delay(10),
|
||||||
|
(TEON, 0x00),
|
||||||
|
(NORON,),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
T4_S3_AMOLED = RM690B0.extend("T4-S3", width=450, offset_width=16, bus_mode=TYPE_QUAD)
|
||||||
|
|
||||||
|
models = {}
|
82
esphome/components/mipi_spi/models/commands.py
Normal file
82
esphome/components/mipi_spi/models/commands.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# MIPI DBI commands
|
||||||
|
|
||||||
|
NOP = 0x00
|
||||||
|
SWRESET = 0x01
|
||||||
|
RDDID = 0x04
|
||||||
|
RDDST = 0x09
|
||||||
|
RDMODE = 0x0A
|
||||||
|
RDMADCTL = 0x0B
|
||||||
|
RDPIXFMT = 0x0C
|
||||||
|
RDIMGFMT = 0x0D
|
||||||
|
RDSELFDIAG = 0x0F
|
||||||
|
SLEEP_IN = 0x10
|
||||||
|
SLPIN = 0x10
|
||||||
|
SLEEP_OUT = 0x11
|
||||||
|
SLPOUT = 0x11
|
||||||
|
PTLON = 0x12
|
||||||
|
NORON = 0x13
|
||||||
|
INVERT_OFF = 0x20
|
||||||
|
INVOFF = 0x20
|
||||||
|
INVERT_ON = 0x21
|
||||||
|
INVON = 0x21
|
||||||
|
ALL_ON = 0x23
|
||||||
|
WRAM = 0x24
|
||||||
|
GAMMASET = 0x26
|
||||||
|
MIPI = 0x26
|
||||||
|
DISPOFF = 0x28
|
||||||
|
DISPON = 0x29
|
||||||
|
CASET = 0x2A
|
||||||
|
PASET = 0x2B
|
||||||
|
RASET = 0x2B
|
||||||
|
RAMWR = 0x2C
|
||||||
|
WDATA = 0x2C
|
||||||
|
RAMRD = 0x2E
|
||||||
|
PTLAR = 0x30
|
||||||
|
VSCRDEF = 0x33
|
||||||
|
TEON = 0x35
|
||||||
|
MADCTL = 0x36
|
||||||
|
MADCTL_CMD = 0x36
|
||||||
|
VSCRSADD = 0x37
|
||||||
|
IDMOFF = 0x38
|
||||||
|
IDMON = 0x39
|
||||||
|
COLMOD = 0x3A
|
||||||
|
PIXFMT = 0x3A
|
||||||
|
GETSCANLINE = 0x45
|
||||||
|
BRIGHTNESS = 0x51
|
||||||
|
WRDISBV = 0x51
|
||||||
|
RDDISBV = 0x52
|
||||||
|
WRCTRLD = 0x53
|
||||||
|
SWIRE1 = 0x5A
|
||||||
|
SWIRE2 = 0x5B
|
||||||
|
IFMODE = 0xB0
|
||||||
|
FRMCTR1 = 0xB1
|
||||||
|
FRMCTR2 = 0xB2
|
||||||
|
FRMCTR3 = 0xB3
|
||||||
|
INVCTR = 0xB4
|
||||||
|
DFUNCTR = 0xB6
|
||||||
|
ETMOD = 0xB7
|
||||||
|
PWCTR1 = 0xC0
|
||||||
|
PWCTR2 = 0xC1
|
||||||
|
PWCTR3 = 0xC2
|
||||||
|
PWCTR4 = 0xC3
|
||||||
|
PWCTR5 = 0xC4
|
||||||
|
VMCTR1 = 0xC5
|
||||||
|
IFCTR = 0xC6
|
||||||
|
VMCTR2 = 0xC7
|
||||||
|
GMCTR = 0xC8
|
||||||
|
SETEXTC = 0xC8
|
||||||
|
PWSET = 0xD0
|
||||||
|
VMCTR = 0xD1
|
||||||
|
PWSETN = 0xD2
|
||||||
|
RDID4 = 0xD3
|
||||||
|
RDINDEX = 0xD9
|
||||||
|
RDID1 = 0xDA
|
||||||
|
RDID2 = 0xDB
|
||||||
|
RDID3 = 0xDC
|
||||||
|
RDIDX = 0xDD
|
||||||
|
GMCTRP1 = 0xE0
|
||||||
|
GMCTRN1 = 0xE1
|
||||||
|
CSCON = 0xF0
|
||||||
|
PWCTR6 = 0xF6
|
||||||
|
ADJCTL3 = 0xF7
|
||||||
|
PAGESEL = 0xFE
|
10
esphome/components/mipi_spi/models/cyd.py
Normal file
10
esphome/components/mipi_spi/models/cyd.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from .ili import ILI9341
|
||||||
|
|
||||||
|
ILI9341.extend(
|
||||||
|
"ESP32-2432S028",
|
||||||
|
data_rate="40MHz",
|
||||||
|
cs_pin=15,
|
||||||
|
dc_pin=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
models = {}
|
749
esphome/components/mipi_spi/models/ili.py
Normal file
749
esphome/components/mipi_spi/models/ili.py
Normal file
@ -0,0 +1,749 @@
|
|||||||
|
from esphome.components.spi import TYPE_OCTAL
|
||||||
|
|
||||||
|
from .. import MODE_RGB
|
||||||
|
from . import DriverChip, delay
|
||||||
|
from .commands import (
|
||||||
|
ADJCTL3,
|
||||||
|
CSCON,
|
||||||
|
DFUNCTR,
|
||||||
|
ETMOD,
|
||||||
|
FRMCTR1,
|
||||||
|
FRMCTR2,
|
||||||
|
FRMCTR3,
|
||||||
|
GAMMASET,
|
||||||
|
GMCTR,
|
||||||
|
GMCTRN1,
|
||||||
|
GMCTRP1,
|
||||||
|
IDMOFF,
|
||||||
|
IFCTR,
|
||||||
|
IFMODE,
|
||||||
|
INVCTR,
|
||||||
|
NORON,
|
||||||
|
PWCTR1,
|
||||||
|
PWCTR2,
|
||||||
|
PWCTR3,
|
||||||
|
PWCTR4,
|
||||||
|
PWCTR5,
|
||||||
|
PWSET,
|
||||||
|
PWSETN,
|
||||||
|
SETEXTC,
|
||||||
|
SWRESET,
|
||||||
|
VMCTR,
|
||||||
|
VMCTR1,
|
||||||
|
VMCTR2,
|
||||||
|
VSCRSADD,
|
||||||
|
)
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
"M5CORE",
|
||||||
|
width=320,
|
||||||
|
height=240,
|
||||||
|
cs_pin=14,
|
||||||
|
dc_pin=27,
|
||||||
|
reset_pin=33,
|
||||||
|
initsequence=(
|
||||||
|
(SETEXTC, 0xFF, 0x93, 0x42),
|
||||||
|
(PWCTR1, 0x12, 0x12),
|
||||||
|
(PWCTR2, 0x03),
|
||||||
|
(VMCTR1, 0xF2),
|
||||||
|
(IFMODE, 0xE0),
|
||||||
|
(0xF6, 0x01, 0x00, 0x00),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x00,
|
||||||
|
0x0C,
|
||||||
|
0x11,
|
||||||
|
0x04,
|
||||||
|
0x11,
|
||||||
|
0x08,
|
||||||
|
0x37,
|
||||||
|
0x89,
|
||||||
|
0x4C,
|
||||||
|
0x06,
|
||||||
|
0x0C,
|
||||||
|
0x0A,
|
||||||
|
0x2E,
|
||||||
|
0x34,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x00,
|
||||||
|
0x0B,
|
||||||
|
0x11,
|
||||||
|
0x05,
|
||||||
|
0x13,
|
||||||
|
0x09,
|
||||||
|
0x33,
|
||||||
|
0x67,
|
||||||
|
0x48,
|
||||||
|
0x07,
|
||||||
|
0x0E,
|
||||||
|
0x0B,
|
||||||
|
0x2E,
|
||||||
|
0x33,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
(DFUNCTR, 0x08, 0x82, 0x1D, 0x04),
|
||||||
|
(IDMOFF,),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ILI9341 = DriverChip(
|
||||||
|
"ILI9341",
|
||||||
|
mirror_x=True,
|
||||||
|
width=240,
|
||||||
|
height=320,
|
||||||
|
initsequence=(
|
||||||
|
(0xEF, 0x03, 0x80, 0x02),
|
||||||
|
(0xCF, 0x00, 0xC1, 0x30),
|
||||||
|
(0xED, 0x64, 0x03, 0x12, 0x81),
|
||||||
|
(0xE8, 0x85, 0x00, 0x78),
|
||||||
|
(0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02),
|
||||||
|
(0xF7, 0x20),
|
||||||
|
(0xEA, 0x00, 0x00),
|
||||||
|
(PWCTR1, 0x23),
|
||||||
|
(PWCTR2, 0x10),
|
||||||
|
(VMCTR1, 0x3E, 0x28),
|
||||||
|
(VMCTR2, 0x86),
|
||||||
|
(VSCRSADD, 0x00),
|
||||||
|
(FRMCTR1, 0x00, 0x18),
|
||||||
|
(DFUNCTR, 0x08, 0x82, 0x27),
|
||||||
|
(0xF2, 0x00),
|
||||||
|
(GAMMASET, 0x01),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x0F,
|
||||||
|
0x31,
|
||||||
|
0x2B,
|
||||||
|
0x0C,
|
||||||
|
0x0E,
|
||||||
|
0x08,
|
||||||
|
0x4E,
|
||||||
|
0xF1,
|
||||||
|
0x37,
|
||||||
|
0x07,
|
||||||
|
0x10,
|
||||||
|
0x03,
|
||||||
|
0x0E,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x00,
|
||||||
|
0x0E,
|
||||||
|
0x14,
|
||||||
|
0x03,
|
||||||
|
0x11,
|
||||||
|
0x07,
|
||||||
|
0x31,
|
||||||
|
0xC1,
|
||||||
|
0x48,
|
||||||
|
0x08,
|
||||||
|
0x0F,
|
||||||
|
0x0C,
|
||||||
|
0x31,
|
||||||
|
0x36,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"ILI9481",
|
||||||
|
mirror_x=True,
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
use_axis_flips=True,
|
||||||
|
initsequence=(
|
||||||
|
(PWSET, 0x07, 0x42, 0x18),
|
||||||
|
(VMCTR, 0x00, 0x07, 0x10),
|
||||||
|
(PWSETN, 0x01, 0x02),
|
||||||
|
(PWCTR1, 0x10, 0x3B, 0x00, 0x02, 0x11),
|
||||||
|
(VMCTR1, 0x03),
|
||||||
|
(IFCTR, 0x83),
|
||||||
|
(GMCTR, 0x32, 0x36, 0x45, 0x06, 0x16, 0x37, 0x75, 0x77, 0x54, 0x0C, 0x00),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"ILI9486",
|
||||||
|
mirror_x=True,
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
initsequence=(
|
||||||
|
(PWCTR3, 0x44),
|
||||||
|
(VMCTR1, 0x00, 0x00, 0x00, 0x00),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x0F,
|
||||||
|
0x1F,
|
||||||
|
0x1C,
|
||||||
|
0x0C,
|
||||||
|
0x0F,
|
||||||
|
0x08,
|
||||||
|
0x48,
|
||||||
|
0x98,
|
||||||
|
0x37,
|
||||||
|
0x0A,
|
||||||
|
0x13,
|
||||||
|
0x04,
|
||||||
|
0x11,
|
||||||
|
0x0D,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x0F,
|
||||||
|
0x32,
|
||||||
|
0x2E,
|
||||||
|
0x0B,
|
||||||
|
0x0D,
|
||||||
|
0x05,
|
||||||
|
0x47,
|
||||||
|
0x75,
|
||||||
|
0x37,
|
||||||
|
0x06,
|
||||||
|
0x10,
|
||||||
|
0x03,
|
||||||
|
0x24,
|
||||||
|
0x20,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"ILI9488",
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
pixel_mode="18bit",
|
||||||
|
initsequence=(
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x0F,
|
||||||
|
0x24,
|
||||||
|
0x1C,
|
||||||
|
0x0A,
|
||||||
|
0x0F,
|
||||||
|
0x08,
|
||||||
|
0x43,
|
||||||
|
0x88,
|
||||||
|
0x32,
|
||||||
|
0x0F,
|
||||||
|
0x10,
|
||||||
|
0x06,
|
||||||
|
0x0F,
|
||||||
|
0x07,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x0F,
|
||||||
|
0x38,
|
||||||
|
0x30,
|
||||||
|
0x09,
|
||||||
|
0x0F,
|
||||||
|
0x0F,
|
||||||
|
0x4E,
|
||||||
|
0x77,
|
||||||
|
0x3C,
|
||||||
|
0x07,
|
||||||
|
0x10,
|
||||||
|
0x05,
|
||||||
|
0x23,
|
||||||
|
0x1B,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(PWCTR1, 0x17, 0x15),
|
||||||
|
(PWCTR2, 0x41),
|
||||||
|
(VMCTR1, 0x00, 0x12, 0x80),
|
||||||
|
(IFMODE, 0x00),
|
||||||
|
(FRMCTR1, 0xA0),
|
||||||
|
(INVCTR, 0x02),
|
||||||
|
(0xE9, 0x00),
|
||||||
|
(ADJCTL3, 0xA9, 0x51, 0x2C, 0x82),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ILI9488_A = DriverChip(
|
||||||
|
"ILI9488_A",
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
invert_colors=False,
|
||||||
|
pixel_mode="18bit",
|
||||||
|
mirror_x=True,
|
||||||
|
initsequence=(
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x00,
|
||||||
|
0x03,
|
||||||
|
0x09,
|
||||||
|
0x08,
|
||||||
|
0x16,
|
||||||
|
0x0A,
|
||||||
|
0x3F,
|
||||||
|
0x78,
|
||||||
|
0x4C,
|
||||||
|
0x09,
|
||||||
|
0x0A,
|
||||||
|
0x08,
|
||||||
|
0x16,
|
||||||
|
0x1A,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x00,
|
||||||
|
0x16,
|
||||||
|
0x19,
|
||||||
|
0x03,
|
||||||
|
0x0F,
|
||||||
|
0x05,
|
||||||
|
0x32,
|
||||||
|
0x45,
|
||||||
|
0x46,
|
||||||
|
0x04,
|
||||||
|
0x0E,
|
||||||
|
0x0D,
|
||||||
|
0x35,
|
||||||
|
0x37,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
(PWCTR1, 0x17, 0x15),
|
||||||
|
(PWCTR2, 0x41),
|
||||||
|
(VMCTR1, 0x00, 0x12, 0x80),
|
||||||
|
(IFMODE, 0x00),
|
||||||
|
(FRMCTR1, 0xA0),
|
||||||
|
(INVCTR, 0x02),
|
||||||
|
(DFUNCTR, 0x02, 0x02),
|
||||||
|
(0xE9, 0x00),
|
||||||
|
(ADJCTL3, 0xA9, 0x51, 0x2C, 0x82),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ST7796 = DriverChip(
|
||||||
|
"ST7796",
|
||||||
|
mirror_x=True,
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
initsequence=(
|
||||||
|
(SWRESET,),
|
||||||
|
(CSCON, 0xC3),
|
||||||
|
(CSCON, 0x96),
|
||||||
|
(VMCTR1, 0x1C),
|
||||||
|
(IFMODE, 0x80),
|
||||||
|
(INVCTR, 0x01),
|
||||||
|
(DFUNCTR, 0x80, 0x02, 0x3B),
|
||||||
|
(ETMOD, 0xC6),
|
||||||
|
(CSCON, 0x69),
|
||||||
|
(CSCON, 0x3C),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"S3BOX",
|
||||||
|
width=320,
|
||||||
|
height=240,
|
||||||
|
mirror_x=True,
|
||||||
|
mirror_y=True,
|
||||||
|
invert_colors=False,
|
||||||
|
data_rate="40MHz",
|
||||||
|
dc_pin=4,
|
||||||
|
cs_pin=5,
|
||||||
|
# reset_pin={CONF_INVERTED: True, CONF_NUMBER: 48},
|
||||||
|
initsequence=(
|
||||||
|
(0xEF, 0x03, 0x80, 0x02),
|
||||||
|
(0xCF, 0x00, 0xC1, 0x30),
|
||||||
|
(0xED, 0x64, 0x03, 0x12, 0x81),
|
||||||
|
(0xE8, 0x85, 0x00, 0x78),
|
||||||
|
(0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02),
|
||||||
|
(0xF7, 0x20),
|
||||||
|
(0xEA, 0x00, 0x00),
|
||||||
|
(PWCTR1, 0x23),
|
||||||
|
(PWCTR2, 0x10),
|
||||||
|
(VMCTR1, 0x3E, 0x28),
|
||||||
|
(VMCTR2, 0x86),
|
||||||
|
(VSCRSADD, 0x00),
|
||||||
|
(FRMCTR1, 0x00, 0x18),
|
||||||
|
(DFUNCTR, 0x08, 0x82, 0x27),
|
||||||
|
(0xF2, 0x00),
|
||||||
|
(GAMMASET, 0x01),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x0F,
|
||||||
|
0x31,
|
||||||
|
0x2B,
|
||||||
|
0x0C,
|
||||||
|
0x0E,
|
||||||
|
0x08,
|
||||||
|
0x4E,
|
||||||
|
0xF1,
|
||||||
|
0x37,
|
||||||
|
0x07,
|
||||||
|
0x10,
|
||||||
|
0x03,
|
||||||
|
0x0E,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x00,
|
||||||
|
0x0E,
|
||||||
|
0x14,
|
||||||
|
0x03,
|
||||||
|
0x11,
|
||||||
|
0x07,
|
||||||
|
0x31,
|
||||||
|
0xC1,
|
||||||
|
0x48,
|
||||||
|
0x08,
|
||||||
|
0x0F,
|
||||||
|
0x0C,
|
||||||
|
0x31,
|
||||||
|
0x36,
|
||||||
|
0x0F,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"S3BOXLITE",
|
||||||
|
mirror_x=True,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
width=320,
|
||||||
|
height=240,
|
||||||
|
cs_pin=5,
|
||||||
|
dc_pin=4,
|
||||||
|
reset_pin=48,
|
||||||
|
initsequence=(
|
||||||
|
(0xEF, 0x03, 0x80, 0x02),
|
||||||
|
(0xCF, 0x00, 0xC1, 0x30),
|
||||||
|
(0xED, 0x64, 0x03, 0x12, 0x81),
|
||||||
|
(0xE8, 0x85, 0x00, 0x78),
|
||||||
|
(0xCB, 0x39, 0x2C, 0x00, 0x34, 0x02),
|
||||||
|
(0xF7, 0x20),
|
||||||
|
(0xEA, 0x00, 0x00),
|
||||||
|
(PWCTR1, 0x23),
|
||||||
|
(PWCTR2, 0x10),
|
||||||
|
(VMCTR1, 0x3E, 0x28),
|
||||||
|
(VMCTR2, 0x86),
|
||||||
|
(VSCRSADD, 0x00),
|
||||||
|
(FRMCTR1, 0x00, 0x18),
|
||||||
|
(DFUNCTR, 0x08, 0x82, 0x27),
|
||||||
|
(0xF2, 0x00),
|
||||||
|
(GAMMASET, 0x01),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0xF0,
|
||||||
|
0x09,
|
||||||
|
0x0B,
|
||||||
|
0x06,
|
||||||
|
0x04,
|
||||||
|
0x15,
|
||||||
|
0x2F,
|
||||||
|
0x54,
|
||||||
|
0x42,
|
||||||
|
0x3C,
|
||||||
|
0x17,
|
||||||
|
0x14,
|
||||||
|
0x18,
|
||||||
|
0x1B,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0xE0,
|
||||||
|
0x09,
|
||||||
|
0x0B,
|
||||||
|
0x06,
|
||||||
|
0x04,
|
||||||
|
0x03,
|
||||||
|
0x2B,
|
||||||
|
0x43,
|
||||||
|
0x42,
|
||||||
|
0x3B,
|
||||||
|
0x16,
|
||||||
|
0x14,
|
||||||
|
0x17,
|
||||||
|
0x1B,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ST7789V = DriverChip(
|
||||||
|
"ST7789V",
|
||||||
|
width=240,
|
||||||
|
height=320,
|
||||||
|
initsequence=(
|
||||||
|
(DFUNCTR, 0x0A, 0x82),
|
||||||
|
(FRMCTR2, 0x0C, 0x0C, 0x00, 0x33, 0x33),
|
||||||
|
(ETMOD, 0x35),
|
||||||
|
(0xBB, 0x28),
|
||||||
|
(PWCTR1, 0x0C),
|
||||||
|
(PWCTR3, 0x01, 0xFF),
|
||||||
|
(PWCTR4, 0x10),
|
||||||
|
(PWCTR5, 0x20),
|
||||||
|
(IFCTR, 0x0F),
|
||||||
|
(PWSET, 0xA4, 0xA1),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0xD0,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x07,
|
||||||
|
0x0A,
|
||||||
|
0x28,
|
||||||
|
0x32,
|
||||||
|
0x44,
|
||||||
|
0x42,
|
||||||
|
0x06,
|
||||||
|
0x0E,
|
||||||
|
0x12,
|
||||||
|
0x14,
|
||||||
|
0x17,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0xD0,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x07,
|
||||||
|
0x0A,
|
||||||
|
0x28,
|
||||||
|
0x31,
|
||||||
|
0x54,
|
||||||
|
0x47,
|
||||||
|
0x0E,
|
||||||
|
0x1C,
|
||||||
|
0x17,
|
||||||
|
0x1B,
|
||||||
|
0x1E,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"GC9A01A",
|
||||||
|
mirror_x=True,
|
||||||
|
width=240,
|
||||||
|
height=240,
|
||||||
|
initsequence=(
|
||||||
|
(0xEF,),
|
||||||
|
(0xEB, 0x14),
|
||||||
|
(0xFE,),
|
||||||
|
(0xEF,),
|
||||||
|
(0xEB, 0x14),
|
||||||
|
(0x84, 0x40),
|
||||||
|
(0x85, 0xFF),
|
||||||
|
(0x86, 0xFF),
|
||||||
|
(0x87, 0xFF),
|
||||||
|
(0x88, 0x0A),
|
||||||
|
(0x89, 0x21),
|
||||||
|
(0x8A, 0x00),
|
||||||
|
(0x8B, 0x80),
|
||||||
|
(0x8C, 0x01),
|
||||||
|
(0x8D, 0x01),
|
||||||
|
(0x8E, 0xFF),
|
||||||
|
(0x8F, 0xFF),
|
||||||
|
(0xB6, 0x00, 0x00),
|
||||||
|
(0x90, 0x08, 0x08, 0x08, 0x08),
|
||||||
|
(0xBD, 0x06),
|
||||||
|
(0xBC, 0x00),
|
||||||
|
(0xFF, 0x60, 0x01, 0x04),
|
||||||
|
(0xC3, 0x13),
|
||||||
|
(0xC4, 0x13),
|
||||||
|
(0xF9, 0x22),
|
||||||
|
(0xBE, 0x11),
|
||||||
|
(0xE1, 0x10, 0x0E),
|
||||||
|
(0xDF, 0x21, 0x0C, 0x02),
|
||||||
|
(0xF0, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A),
|
||||||
|
(0xF1, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F),
|
||||||
|
(0xF2, 0x45, 0x09, 0x08, 0x08, 0x26, 0x2A),
|
||||||
|
(0xF3, 0x43, 0x70, 0x72, 0x36, 0x37, 0x6F),
|
||||||
|
(0xED, 0x1B, 0x0B),
|
||||||
|
(0xAE, 0x77),
|
||||||
|
(0xCD, 0x63),
|
||||||
|
(0xE8, 0x34),
|
||||||
|
(
|
||||||
|
0x62,
|
||||||
|
0x18,
|
||||||
|
0x0D,
|
||||||
|
0x71,
|
||||||
|
0xED,
|
||||||
|
0x70,
|
||||||
|
0x70,
|
||||||
|
0x18,
|
||||||
|
0x0F,
|
||||||
|
0x71,
|
||||||
|
0xEF,
|
||||||
|
0x70,
|
||||||
|
0x70,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0x63,
|
||||||
|
0x18,
|
||||||
|
0x11,
|
||||||
|
0x71,
|
||||||
|
0xF1,
|
||||||
|
0x70,
|
||||||
|
0x70,
|
||||||
|
0x18,
|
||||||
|
0x13,
|
||||||
|
0x71,
|
||||||
|
0xF3,
|
||||||
|
0x70,
|
||||||
|
0x70,
|
||||||
|
),
|
||||||
|
(0x64, 0x28, 0x29, 0xF1, 0x01, 0xF1, 0x00, 0x07),
|
||||||
|
(0x66, 0x3C, 0x00, 0xCD, 0x67, 0x45, 0x45, 0x10, 0x00, 0x00, 0x00),
|
||||||
|
(0x67, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x54, 0x10, 0x32, 0x98),
|
||||||
|
(0x74, 0x10, 0x85, 0x80, 0x00, 0x00, 0x4E, 0x00),
|
||||||
|
(0x98, 0x3E, 0x07),
|
||||||
|
(0x35,),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"GC9D01N",
|
||||||
|
width=160,
|
||||||
|
height=160,
|
||||||
|
initsequence=(
|
||||||
|
(0xFE,),
|
||||||
|
(0xEF,),
|
||||||
|
(0x80, 0xFF),
|
||||||
|
(0x81, 0xFF),
|
||||||
|
(0x82, 0xFF),
|
||||||
|
(0x83, 0xFF),
|
||||||
|
(0x84, 0xFF),
|
||||||
|
(0x85, 0xFF),
|
||||||
|
(0x86, 0xFF),
|
||||||
|
(0x87, 0xFF),
|
||||||
|
(0x88, 0xFF),
|
||||||
|
(0x89, 0xFF),
|
||||||
|
(0x8A, 0xFF),
|
||||||
|
(0x8B, 0xFF),
|
||||||
|
(0x8C, 0xFF),
|
||||||
|
(0x8D, 0xFF),
|
||||||
|
(0x8E, 0xFF),
|
||||||
|
(0x8F, 0xFF),
|
||||||
|
(0x3A, 0x05),
|
||||||
|
(0xEC, 0x01),
|
||||||
|
(0x74, 0x02, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||||
|
(0x98, 0x3E),
|
||||||
|
(0x99, 0x3E),
|
||||||
|
(0xB5, 0x0D, 0x0D),
|
||||||
|
(0x60, 0x38, 0x0F, 0x79, 0x67),
|
||||||
|
(0x61, 0x38, 0x11, 0x79, 0x67),
|
||||||
|
(0x64, 0x38, 0x17, 0x71, 0x5F, 0x79, 0x67),
|
||||||
|
(0x65, 0x38, 0x13, 0x71, 0x5B, 0x79, 0x67),
|
||||||
|
(0x6A, 0x00, 0x00),
|
||||||
|
(0x6C, 0x22, 0x02, 0x22, 0x02, 0x22, 0x22, 0x50),
|
||||||
|
(
|
||||||
|
0x6E,
|
||||||
|
0x03,
|
||||||
|
0x03,
|
||||||
|
0x01,
|
||||||
|
0x01,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0F,
|
||||||
|
0x0F,
|
||||||
|
0x0D,
|
||||||
|
0x0D,
|
||||||
|
0x0B,
|
||||||
|
0x0B,
|
||||||
|
0x09,
|
||||||
|
0x09,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x0A,
|
||||||
|
0x0A,
|
||||||
|
0x0C,
|
||||||
|
0x0C,
|
||||||
|
0x0E,
|
||||||
|
0x0E,
|
||||||
|
0x10,
|
||||||
|
0x10,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x02,
|
||||||
|
0x04,
|
||||||
|
0x04,
|
||||||
|
),
|
||||||
|
(0xBF, 0x01),
|
||||||
|
(0xF9, 0x40),
|
||||||
|
(0x9B, 0x3B, 0x93, 0x33, 0x7F, 0x00),
|
||||||
|
(0x7E, 0x30),
|
||||||
|
(0x70, 0x0D, 0x02, 0x08, 0x0D, 0x02, 0x08),
|
||||||
|
(0x71, 0x0D, 0x02, 0x08),
|
||||||
|
(0x91, 0x0E, 0x09),
|
||||||
|
(0xC3, 0x19, 0xC4, 0x19, 0xC9, 0x3C),
|
||||||
|
(0xF0, 0x53, 0x15, 0x0A, 0x04, 0x00, 0x3E),
|
||||||
|
(0xF1, 0x56, 0xA8, 0x7F, 0x33, 0x34, 0x5F),
|
||||||
|
(0xF2, 0x53, 0x15, 0x0A, 0x04, 0x00, 0x3A),
|
||||||
|
(0xF3, 0x52, 0xA4, 0x7F, 0x33, 0x34, 0xDF),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
DriverChip(
|
||||||
|
"ST7735",
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
width=128,
|
||||||
|
height=160,
|
||||||
|
initsequence=(
|
||||||
|
SWRESET,
|
||||||
|
delay(10),
|
||||||
|
(FRMCTR1, 0x01, 0x2C, 0x2D),
|
||||||
|
(FRMCTR2, 0x01, 0x2C, 0x2D),
|
||||||
|
(FRMCTR3, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D),
|
||||||
|
(INVCTR, 0x07),
|
||||||
|
(PWCTR1, 0xA2, 0x02, 0x84),
|
||||||
|
(PWCTR2, 0xC5),
|
||||||
|
(PWCTR3, 0x0A, 0x00),
|
||||||
|
(PWCTR4, 0x8A, 0x2A),
|
||||||
|
(PWCTR5, 0x8A, 0xEE),
|
||||||
|
(VMCTR1, 0x0E),
|
||||||
|
(
|
||||||
|
GMCTRP1,
|
||||||
|
0x02,
|
||||||
|
0x1C,
|
||||||
|
0x07,
|
||||||
|
0x12,
|
||||||
|
0x37,
|
||||||
|
0x32,
|
||||||
|
0x29,
|
||||||
|
0x2D,
|
||||||
|
0x29,
|
||||||
|
0x25,
|
||||||
|
0x2B,
|
||||||
|
0x39,
|
||||||
|
0x00,
|
||||||
|
0x01,
|
||||||
|
0x03,
|
||||||
|
0x10,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
GMCTRN1,
|
||||||
|
0x03,
|
||||||
|
0x1D,
|
||||||
|
0x07,
|
||||||
|
0x06,
|
||||||
|
0x2E,
|
||||||
|
0x2C,
|
||||||
|
0x29,
|
||||||
|
0x2D,
|
||||||
|
0x2E,
|
||||||
|
0x2E,
|
||||||
|
0x37,
|
||||||
|
0x3F,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
0x10,
|
||||||
|
),
|
||||||
|
NORON,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
ST7796.extend(
|
||||||
|
"WT32-SC01-PLUS",
|
||||||
|
bus_mode=TYPE_OCTAL,
|
||||||
|
mirror_x=True,
|
||||||
|
reset_pin=4,
|
||||||
|
dc_pin=0,
|
||||||
|
invert_colors=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
models = {}
|
260
esphome/components/mipi_spi/models/jc.py
Normal file
260
esphome/components/mipi_spi/models/jc.py
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
from esphome.components.spi import TYPE_QUAD
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_IGNORE_STRAPPING_WARNING, CONF_NUMBER
|
||||||
|
|
||||||
|
from .. import MODE_RGB
|
||||||
|
from . import DriverChip
|
||||||
|
|
||||||
|
AXS15231 = DriverChip(
|
||||||
|
"AXS15231",
|
||||||
|
draw_rounding=8,
|
||||||
|
swap_xy=cv.UNDEFINED,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
bus_mode=TYPE_QUAD,
|
||||||
|
initsequence=(
|
||||||
|
(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, 0xA5),
|
||||||
|
(0xC1, 0x33),
|
||||||
|
(0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
AXS15231.extend(
|
||||||
|
"JC3248W535",
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
cs_pin={CONF_NUMBER: 45, CONF_IGNORE_STRAPPING_WARNING: True},
|
||||||
|
data_rate="40MHz",
|
||||||
|
)
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
"JC3636W518",
|
||||||
|
height=360,
|
||||||
|
width=360,
|
||||||
|
offset_height=1,
|
||||||
|
draw_rounding=1,
|
||||||
|
cs_pin=10,
|
||||||
|
reset_pin=47,
|
||||||
|
invert_colors=True,
|
||||||
|
color_order=MODE_RGB,
|
||||||
|
bus_mode=TYPE_QUAD,
|
||||||
|
data_rate="40MHz",
|
||||||
|
initsequence=(
|
||||||
|
(0xF0, 0x08),
|
||||||
|
(0xF2, 0x08),
|
||||||
|
(0x9B, 0x51),
|
||||||
|
(0x86, 0x53),
|
||||||
|
(0xF2, 0x80),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
(0xF0, 0x01),
|
||||||
|
(0xF1, 0x01),
|
||||||
|
(0xB0, 0x54),
|
||||||
|
(0xB1, 0x3F),
|
||||||
|
(0xB2, 0x2A),
|
||||||
|
(0xB4, 0x46),
|
||||||
|
(0xB5, 0x34),
|
||||||
|
(0xB6, 0xD5),
|
||||||
|
(0xB7, 0x30),
|
||||||
|
(0xBA, 0x00),
|
||||||
|
(0xBB, 0x08),
|
||||||
|
(0xBC, 0x08),
|
||||||
|
(0xBD, 0x00),
|
||||||
|
(0xC0, 0x80),
|
||||||
|
(0xC1, 0x10),
|
||||||
|
(0xC2, 0x37),
|
||||||
|
(0xC3, 0x80),
|
||||||
|
(0xC4, 0x10),
|
||||||
|
(0xC5, 0x37),
|
||||||
|
(0xC6, 0xA9),
|
||||||
|
(0xC7, 0x41),
|
||||||
|
(0xC8, 0x51),
|
||||||
|
(0xC9, 0xA9),
|
||||||
|
(0xCA, 0x41),
|
||||||
|
(0xCB, 0x51),
|
||||||
|
(0xD0, 0x91),
|
||||||
|
(0xD1, 0x68),
|
||||||
|
(0xD2, 0x69),
|
||||||
|
(0xF5, 0x00, 0xA5),
|
||||||
|
(0xDD, 0x3F),
|
||||||
|
(0xDE, 0x3F),
|
||||||
|
(0xF1, 0x10),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
(0xF0, 0x02),
|
||||||
|
(
|
||||||
|
0xE0,
|
||||||
|
0x70,
|
||||||
|
0x09,
|
||||||
|
0x12,
|
||||||
|
0x0C,
|
||||||
|
0x0B,
|
||||||
|
0x27,
|
||||||
|
0x38,
|
||||||
|
0x54,
|
||||||
|
0x4E,
|
||||||
|
0x19,
|
||||||
|
0x15,
|
||||||
|
0x15,
|
||||||
|
0x2C,
|
||||||
|
0x2F,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xE1,
|
||||||
|
0x70,
|
||||||
|
0x08,
|
||||||
|
0x11,
|
||||||
|
0x0C,
|
||||||
|
0x0B,
|
||||||
|
0x27,
|
||||||
|
0x38,
|
||||||
|
0x43,
|
||||||
|
0x4C,
|
||||||
|
0x18,
|
||||||
|
0x14,
|
||||||
|
0x14,
|
||||||
|
0x2B,
|
||||||
|
0x2D,
|
||||||
|
),
|
||||||
|
(0xF0, 0x10),
|
||||||
|
(0xF3, 0x10),
|
||||||
|
(0xE0, 0x08),
|
||||||
|
(0xE1, 0x00),
|
||||||
|
(0xE2, 0x00),
|
||||||
|
(0xE3, 0x00),
|
||||||
|
(0xE4, 0xE0),
|
||||||
|
(0xE5, 0x06),
|
||||||
|
(0xE6, 0x21),
|
||||||
|
(0xE7, 0x00),
|
||||||
|
(0xE8, 0x05),
|
||||||
|
(0xE9, 0x82),
|
||||||
|
(0xEA, 0xDF),
|
||||||
|
(0xEB, 0x89),
|
||||||
|
(0xEC, 0x20),
|
||||||
|
(0xED, 0x14),
|
||||||
|
(0xEE, 0xFF),
|
||||||
|
(0xEF, 0x00),
|
||||||
|
(0xF8, 0xFF),
|
||||||
|
(0xF9, 0x00),
|
||||||
|
(0xFA, 0x00),
|
||||||
|
(0xFB, 0x30),
|
||||||
|
(0xFC, 0x00),
|
||||||
|
(0xFD, 0x00),
|
||||||
|
(0xFE, 0x00),
|
||||||
|
(0xFF, 0x00),
|
||||||
|
(0x60, 0x42),
|
||||||
|
(0x61, 0xE0),
|
||||||
|
(0x62, 0x40),
|
||||||
|
(0x63, 0x40),
|
||||||
|
(0x64, 0x02),
|
||||||
|
(0x65, 0x00),
|
||||||
|
(0x66, 0x40),
|
||||||
|
(0x67, 0x03),
|
||||||
|
(0x68, 0x00),
|
||||||
|
(0x69, 0x00),
|
||||||
|
(0x6A, 0x00),
|
||||||
|
(0x6B, 0x00),
|
||||||
|
(0x70, 0x42),
|
||||||
|
(0x71, 0xE0),
|
||||||
|
(0x72, 0x40),
|
||||||
|
(0x73, 0x40),
|
||||||
|
(0x74, 0x02),
|
||||||
|
(0x75, 0x00),
|
||||||
|
(0x76, 0x40),
|
||||||
|
(0x77, 0x03),
|
||||||
|
(0x78, 0x00),
|
||||||
|
(0x79, 0x00),
|
||||||
|
(0x7A, 0x00),
|
||||||
|
(0x7B, 0x00),
|
||||||
|
(0x80, 0x48),
|
||||||
|
(0x81, 0x00),
|
||||||
|
(0x82, 0x05),
|
||||||
|
(0x83, 0x02),
|
||||||
|
(0x84, 0xDD),
|
||||||
|
(0x85, 0x00),
|
||||||
|
(0x86, 0x00),
|
||||||
|
(0x87, 0x00),
|
||||||
|
(0x88, 0x48),
|
||||||
|
(0x89, 0x00),
|
||||||
|
(0x8A, 0x07),
|
||||||
|
(0x8B, 0x02),
|
||||||
|
(0x8C, 0xDF),
|
||||||
|
(0x8D, 0x00),
|
||||||
|
(0x8E, 0x00),
|
||||||
|
(0x8F, 0x00),
|
||||||
|
(0x90, 0x48),
|
||||||
|
(0x91, 0x00),
|
||||||
|
(0x92, 0x09),
|
||||||
|
(0x93, 0x02),
|
||||||
|
(0x94, 0xE1),
|
||||||
|
(0x95, 0x00),
|
||||||
|
(0x96, 0x00),
|
||||||
|
(0x97, 0x00),
|
||||||
|
(0x98, 0x48),
|
||||||
|
(0x99, 0x00),
|
||||||
|
(0x9A, 0x0B),
|
||||||
|
(0x9B, 0x02),
|
||||||
|
(0x9C, 0xE3),
|
||||||
|
(0x9D, 0x00),
|
||||||
|
(0x9E, 0x00),
|
||||||
|
(0x9F, 0x00),
|
||||||
|
(0xA0, 0x48),
|
||||||
|
(0xA1, 0x00),
|
||||||
|
(0xA2, 0x04),
|
||||||
|
(0xA3, 0x02),
|
||||||
|
(0xA4, 0xDC),
|
||||||
|
(0xA5, 0x00),
|
||||||
|
(0xA6, 0x00),
|
||||||
|
(0xA7, 0x00),
|
||||||
|
(0xA8, 0x48),
|
||||||
|
(0xA9, 0x00),
|
||||||
|
(0xAA, 0x06),
|
||||||
|
(0xAB, 0x02),
|
||||||
|
(0xAC, 0xDE),
|
||||||
|
(0xAD, 0x00),
|
||||||
|
(0xAE, 0x00),
|
||||||
|
(0xAF, 0x00),
|
||||||
|
(0xB0, 0x48),
|
||||||
|
(0xB1, 0x00),
|
||||||
|
(0xB2, 0x08),
|
||||||
|
(0xB3, 0x02),
|
||||||
|
(0xB4, 0xE0),
|
||||||
|
(0xB5, 0x00),
|
||||||
|
(0xB6, 0x00),
|
||||||
|
(0xB7, 0x00),
|
||||||
|
(0xB8, 0x48),
|
||||||
|
(0xB9, 0x00),
|
||||||
|
(0xBA, 0x0A),
|
||||||
|
(0xBB, 0x02),
|
||||||
|
(0xBC, 0xE2),
|
||||||
|
(0xBD, 0x00),
|
||||||
|
(0xBE, 0x00),
|
||||||
|
(0xBF, 0x00),
|
||||||
|
(0xC0, 0x12),
|
||||||
|
(0xC1, 0xAA),
|
||||||
|
(0xC2, 0x65),
|
||||||
|
(0xC3, 0x74),
|
||||||
|
(0xC4, 0x47),
|
||||||
|
(0xC5, 0x56),
|
||||||
|
(0xC6, 0x00),
|
||||||
|
(0xC7, 0x88),
|
||||||
|
(0xC8, 0x99),
|
||||||
|
(0xC9, 0x33),
|
||||||
|
(0xD0, 0x21),
|
||||||
|
(0xD1, 0xAA),
|
||||||
|
(0xD2, 0x65),
|
||||||
|
(0xD3, 0x74),
|
||||||
|
(0xD4, 0x47),
|
||||||
|
(0xD5, 0x56),
|
||||||
|
(0xD6, 0x00),
|
||||||
|
(0xD7, 0x88),
|
||||||
|
(0xD8, 0x99),
|
||||||
|
(0xD9, 0x33),
|
||||||
|
(0xF3, 0x01),
|
||||||
|
(0xF0, 0x00),
|
||||||
|
(0xF0, 0x01),
|
||||||
|
(0xF1, 0x01),
|
||||||
|
(0xA0, 0x0B),
|
||||||
|
(0xA3, 0x2A),
|
||||||
|
(0xA5, 0xC3),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
models = {}
|
15
esphome/components/mipi_spi/models/lanbon.py
Normal file
15
esphome/components/mipi_spi/models/lanbon.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from .ili import ST7789V
|
||||||
|
|
||||||
|
ST7789V.extend(
|
||||||
|
"LANBON-L8",
|
||||||
|
width=240,
|
||||||
|
height=320,
|
||||||
|
mirror_x=True,
|
||||||
|
mirror_y=True,
|
||||||
|
data_rate="80MHz",
|
||||||
|
cs_pin=22,
|
||||||
|
dc_pin=21,
|
||||||
|
reset_pin=18,
|
||||||
|
)
|
||||||
|
|
||||||
|
models = {}
|
60
esphome/components/mipi_spi/models/lilygo.py
Normal file
60
esphome/components/mipi_spi/models/lilygo.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
from esphome.components.spi import TYPE_OCTAL
|
||||||
|
|
||||||
|
from .. import MODE_BGR
|
||||||
|
from .ili import ST7789V, ST7796
|
||||||
|
|
||||||
|
ST7789V.extend(
|
||||||
|
"T-EMBED",
|
||||||
|
width=170,
|
||||||
|
height=320,
|
||||||
|
offset_width=35,
|
||||||
|
color_order=MODE_BGR,
|
||||||
|
invert_colors=True,
|
||||||
|
draw_rounding=1,
|
||||||
|
cs_pin=10,
|
||||||
|
dc_pin=13,
|
||||||
|
reset_pin=9,
|
||||||
|
data_rate="80MHz",
|
||||||
|
)
|
||||||
|
|
||||||
|
ST7789V.extend(
|
||||||
|
"T-DISPLAY",
|
||||||
|
height=240,
|
||||||
|
width=135,
|
||||||
|
offset_width=52,
|
||||||
|
offset_height=40,
|
||||||
|
draw_rounding=1,
|
||||||
|
cs_pin=5,
|
||||||
|
dc_pin=16,
|
||||||
|
invert_colors=True,
|
||||||
|
)
|
||||||
|
ST7789V.extend(
|
||||||
|
"T-DISPLAY-S3",
|
||||||
|
height=320,
|
||||||
|
width=170,
|
||||||
|
offset_width=35,
|
||||||
|
color_order=MODE_BGR,
|
||||||
|
invert_colors=True,
|
||||||
|
draw_rounding=1,
|
||||||
|
dc_pin=7,
|
||||||
|
cs_pin=6,
|
||||||
|
reset_pin=5,
|
||||||
|
enable_pin=[9, 15],
|
||||||
|
data_rate="10MHz",
|
||||||
|
bus_mode=TYPE_OCTAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
ST7796.extend(
|
||||||
|
"T-DISPLAY-S3-PRO",
|
||||||
|
width=222,
|
||||||
|
height=480,
|
||||||
|
offset_width=49,
|
||||||
|
draw_rounding=1,
|
||||||
|
cs_pin=39,
|
||||||
|
reset_pin=47,
|
||||||
|
dc_pin=9,
|
||||||
|
backlight_pin=48,
|
||||||
|
invert_colors=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
models = {}
|
139
esphome/components/mipi_spi/models/waveshare.py
Normal file
139
esphome/components/mipi_spi/models/waveshare.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
from . import DriverChip
|
||||||
|
from .ili import ILI9488_A
|
||||||
|
|
||||||
|
DriverChip(
|
||||||
|
"WAVESHARE-4-TFT",
|
||||||
|
width=320,
|
||||||
|
height=480,
|
||||||
|
invert_colors=True,
|
||||||
|
spi_16=True,
|
||||||
|
initsequence=(
|
||||||
|
(
|
||||||
|
0xF9,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xC0,
|
||||||
|
0x19,
|
||||||
|
0x1A,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xC1,
|
||||||
|
0x45,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xC2,
|
||||||
|
0x33,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xC5,
|
||||||
|
0x00,
|
||||||
|
0x28,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xB1,
|
||||||
|
0xA0,
|
||||||
|
0x11,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xB4,
|
||||||
|
0x02,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xB6,
|
||||||
|
0x00,
|
||||||
|
0x42,
|
||||||
|
0x3B,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xB7,
|
||||||
|
0x07,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xE0,
|
||||||
|
0x1F,
|
||||||
|
0x25,
|
||||||
|
0x22,
|
||||||
|
0x0B,
|
||||||
|
0x06,
|
||||||
|
0x0A,
|
||||||
|
0x4E,
|
||||||
|
0xC6,
|
||||||
|
0x39,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xE1,
|
||||||
|
0x1F,
|
||||||
|
0x3F,
|
||||||
|
0x3F,
|
||||||
|
0x0F,
|
||||||
|
0x1F,
|
||||||
|
0x0F,
|
||||||
|
0x46,
|
||||||
|
0x49,
|
||||||
|
0x31,
|
||||||
|
0x05,
|
||||||
|
0x09,
|
||||||
|
0x03,
|
||||||
|
0x1C,
|
||||||
|
0x1A,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xF1,
|
||||||
|
0x36,
|
||||||
|
0x04,
|
||||||
|
0x00,
|
||||||
|
0x3C,
|
||||||
|
0x0F,
|
||||||
|
0x0F,
|
||||||
|
0xA4,
|
||||||
|
0x02,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xF2,
|
||||||
|
0x18,
|
||||||
|
0xA3,
|
||||||
|
0x12,
|
||||||
|
0x02,
|
||||||
|
0x32,
|
||||||
|
0x12,
|
||||||
|
0xFF,
|
||||||
|
0x32,
|
||||||
|
0x00,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xF4,
|
||||||
|
0x40,
|
||||||
|
0x00,
|
||||||
|
0x08,
|
||||||
|
0x91,
|
||||||
|
0x04,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xF8,
|
||||||
|
0x21,
|
||||||
|
0x04,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
ILI9488_A.extend(
|
||||||
|
"PICO-RESTOUCH-LCD-3.5",
|
||||||
|
spi_16=True,
|
||||||
|
pixel_mode="16bit",
|
||||||
|
mirror_x=True,
|
||||||
|
dc_pin=33,
|
||||||
|
cs_pin=34,
|
||||||
|
reset_pin=40,
|
||||||
|
data_rate="20MHz",
|
||||||
|
invert_colors=True,
|
||||||
|
)
|
@ -62,6 +62,13 @@ int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) {
|
|||||||
case 1:
|
case 1:
|
||||||
this->width_bytes_ = (this->width_ % 8 == 0) ? (this->width_ / 8) : (this->width_ / 8 + 1);
|
this->width_bytes_ = (this->width_ % 8 == 0) ? (this->width_ / 8) : (this->width_ / 8 + 1);
|
||||||
break;
|
break;
|
||||||
|
case 24:
|
||||||
|
this->width_bytes_ = this->width_ * 3;
|
||||||
|
if (this->width_bytes_ % 4 != 0) {
|
||||||
|
this->padding_bytes_ = 4 - (this->width_bytes_ % 4);
|
||||||
|
this->width_bytes_ += this->padding_bytes_;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
|
ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
|
||||||
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
||||||
@ -78,19 +85,49 @@ int HOT BmpDecoder::decode(uint8_t *buffer, size_t size) {
|
|||||||
this->current_index_ = this->data_offset_;
|
this->current_index_ = this->data_offset_;
|
||||||
index = this->data_offset_;
|
index = this->data_offset_;
|
||||||
}
|
}
|
||||||
|
switch (this->bits_per_pixel_) {
|
||||||
|
case 1: {
|
||||||
while (index < size) {
|
while (index < size) {
|
||||||
size_t paint_index = this->current_index_ - this->data_offset_;
|
|
||||||
|
|
||||||
uint8_t current_byte = buffer[index];
|
uint8_t current_byte = buffer[index];
|
||||||
for (uint8_t i = 0; i < 8; i++) {
|
for (uint8_t i = 0; i < 8; i++) {
|
||||||
size_t x = (paint_index * 8) % this->width_ + i;
|
size_t x = (this->paint_index_ % this->width_) + i;
|
||||||
size_t y = (this->height_ - 1) - (paint_index / this->width_bytes_);
|
size_t y = (this->height_ - 1) - (this->paint_index_ / this->width_);
|
||||||
Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF;
|
Color c = (current_byte & (1 << (7 - i))) ? display::COLOR_ON : display::COLOR_OFF;
|
||||||
this->draw(x, y, 1, 1, c);
|
this->draw(x, y, 1, 1, c);
|
||||||
}
|
}
|
||||||
|
this->paint_index_ += 8;
|
||||||
this->current_index_++;
|
this->current_index_++;
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 24: {
|
||||||
|
while (index < size) {
|
||||||
|
if (index + 2 >= size) {
|
||||||
|
this->decoded_bytes_ += index;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
uint8_t b = buffer[index];
|
||||||
|
uint8_t g = buffer[index + 1];
|
||||||
|
uint8_t r = buffer[index + 2];
|
||||||
|
size_t x = this->paint_index_ % this->width_;
|
||||||
|
size_t y = (this->height_ - 1) - (this->paint_index_ / this->width_);
|
||||||
|
Color c = Color(r, g, b);
|
||||||
|
this->draw(x, y, 1, 1, c);
|
||||||
|
this->paint_index_++;
|
||||||
|
this->current_index_ += 3;
|
||||||
|
index += 3;
|
||||||
|
if (x == this->width_ - 1 && this->padding_bytes_ > 0) {
|
||||||
|
index += this->padding_bytes_;
|
||||||
|
this->current_index_ += this->padding_bytes_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ESP_LOGE(TAG, "Unsupported bits per pixel: %d", this->bits_per_pixel_);
|
||||||
|
return DECODE_ERROR_UNSUPPORTED_FORMAT;
|
||||||
|
}
|
||||||
this->decoded_bytes_ += size;
|
this->decoded_bytes_ += size;
|
||||||
return size;
|
return size;
|
||||||
};
|
};
|
||||||
|
@ -24,6 +24,7 @@ class BmpDecoder : public ImageDecoder {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t current_index_{0};
|
size_t current_index_{0};
|
||||||
|
size_t paint_index_{0};
|
||||||
ssize_t width_{0};
|
ssize_t width_{0};
|
||||||
ssize_t height_{0};
|
ssize_t height_{0};
|
||||||
uint16_t bits_per_pixel_{0};
|
uint16_t bits_per_pixel_{0};
|
||||||
@ -32,6 +33,7 @@ class BmpDecoder : public ImageDecoder {
|
|||||||
uint32_t color_table_entries_{0};
|
uint32_t color_table_entries_{0};
|
||||||
size_t width_bytes_{0};
|
size_t width_bytes_{0};
|
||||||
size_t data_offset_{0};
|
size_t data_offset_{0};
|
||||||
|
uint8_t padding_bytes_{0};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace online_image
|
} // namespace online_image
|
||||||
|
@ -271,9 +271,8 @@ PIPELINE_SCHEMA = cv.Schema(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
media_player.MEDIA_PLAYER_SCHEMA.extend(
|
media_player.media_player_schema(SpeakerMediaPlayer).extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(SpeakerMediaPlayer),
|
|
||||||
cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA,
|
cv.Required(CONF_ANNOUNCEMENT_PIPELINE): PIPELINE_SCHEMA,
|
||||||
cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA,
|
cv.Optional(CONF_MEDIA_PIPELINE): PIPELINE_SCHEMA,
|
||||||
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
cv.Optional(CONF_BUFFER_SIZE, default=1000000): cv.int_range(
|
||||||
@ -343,9 +342,8 @@ async def to_code(config):
|
|||||||
# Allocate wifi buffers in PSRAM
|
# Allocate wifi buffers in PSRAM
|
||||||
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
|
esp32.add_idf_sdkconfig_option("CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP", True)
|
||||||
|
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await media_player.new_media_player(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await media_player.register_media_player(var, config)
|
|
||||||
|
|
||||||
cg.add_define("USE_OTA_STATE_CALLBACK")
|
cg.add_define("USE_OTA_STATE_CALLBACK")
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ from esphome.const import (
|
|||||||
CONF_DIRECTION_OUTPUT,
|
CONF_DIRECTION_OUTPUT,
|
||||||
CONF_OSCILLATION_OUTPUT,
|
CONF_OSCILLATION_OUTPUT,
|
||||||
CONF_OUTPUT,
|
CONF_OUTPUT,
|
||||||
CONF_OUTPUT_ID,
|
|
||||||
CONF_PRESET_MODES,
|
CONF_PRESET_MODES,
|
||||||
CONF_SPEED,
|
CONF_SPEED,
|
||||||
CONF_SPEED_COUNT,
|
CONF_SPEED_COUNT,
|
||||||
@ -16,9 +15,10 @@ from .. import speed_ns
|
|||||||
|
|
||||||
SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
SpeedFan = speed_ns.class_("SpeedFan", cg.Component, fan.Fan)
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
|
fan.fan_schema(SpeedFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(SpeedFan),
|
|
||||||
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
cv.Required(CONF_OUTPUT): cv.use_id(output.FloatOutput),
|
||||||
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_OSCILLATION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
cv.Optional(CONF_DIRECTION_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||||
@ -28,13 +28,14 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
|||||||
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
cv.Optional(CONF_SPEED_COUNT, default=100): cv.int_range(min=1),
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID], config[CONF_SPEED_COUNT])
|
var = await fan.new_fan(config, config[CONF_SPEED_COUNT])
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||||
cg.add(var.set_output(output_))
|
cg.add(var.set_output(output_))
|
||||||
|
@ -355,6 +355,12 @@ class SPIComponent : public Component {
|
|||||||
|
|
||||||
void setup() override;
|
void setup() override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
|
size_t get_bus_width() const {
|
||||||
|
if (this->data_pins_.empty()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return this->data_pins_.size();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
GPIOPin *clk_pin_{nullptr};
|
GPIOPin *clk_pin_{nullptr};
|
||||||
|
@ -72,6 +72,9 @@ _SWITCH_SCHEMA = (
|
|||||||
{
|
{
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
|
||||||
cv.Optional(CONF_INVERTED): cv.boolean,
|
cv.Optional(CONF_INVERTED): cv.boolean,
|
||||||
|
cv.Optional(CONF_RESTORE_MODE, default="ALWAYS_OFF"): cv.enum(
|
||||||
|
RESTORE_MODES, upper=True, space="_"
|
||||||
|
),
|
||||||
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SwitchTurnOnTrigger),
|
||||||
@ -89,54 +92,41 @@ _SWITCH_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
def switch_schema(
|
def switch_schema(
|
||||||
class_: MockObjClass = cv.UNDEFINED,
|
class_: MockObjClass,
|
||||||
*,
|
*,
|
||||||
entity_category: str = cv.UNDEFINED,
|
|
||||||
device_class: str = cv.UNDEFINED,
|
|
||||||
icon: str = cv.UNDEFINED,
|
|
||||||
block_inverted: bool = False,
|
block_inverted: bool = False,
|
||||||
default_restore_mode: str = "ALWAYS_OFF",
|
default_restore_mode: str = cv.UNDEFINED,
|
||||||
|
device_class: str = cv.UNDEFINED,
|
||||||
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
icon: str = cv.UNDEFINED,
|
||||||
):
|
):
|
||||||
schema = _SWITCH_SCHEMA.extend(
|
schema = {cv.GenerateID(): cv.declare_id(class_)}
|
||||||
{
|
|
||||||
cv.Optional(CONF_RESTORE_MODE, default=default_restore_mode): cv.enum(
|
for key, default, validator in [
|
||||||
RESTORE_MODES, upper=True, space="_"
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||||
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
|
(CONF_ICON, icon, cv.icon),
|
||||||
|
(
|
||||||
|
CONF_RESTORE_MODE,
|
||||||
|
default_restore_mode,
|
||||||
|
cv.enum(RESTORE_MODES, upper=True, space="_")
|
||||||
|
if default_restore_mode is not cv.UNDEFINED
|
||||||
|
else cv.UNDEFINED,
|
||||||
),
|
),
|
||||||
}
|
]:
|
||||||
)
|
if default is not cv.UNDEFINED:
|
||||||
if class_ is not cv.UNDEFINED:
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
|
||||||
if entity_category is not cv.UNDEFINED:
|
|
||||||
schema = schema.extend(
|
|
||||||
{
|
|
||||||
cv.Optional(
|
|
||||||
CONF_ENTITY_CATEGORY, default=entity_category
|
|
||||||
): cv.entity_category
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if device_class is not cv.UNDEFINED:
|
|
||||||
schema = schema.extend(
|
|
||||||
{
|
|
||||||
cv.Optional(
|
|
||||||
CONF_DEVICE_CLASS, default=device_class
|
|
||||||
): validate_device_class
|
|
||||||
}
|
|
||||||
)
|
|
||||||
if icon is not cv.UNDEFINED:
|
|
||||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
|
||||||
if block_inverted:
|
if block_inverted:
|
||||||
schema = schema.extend(
|
schema[cv.Optional(CONF_INVERTED)] = cv.invalid(
|
||||||
{
|
|
||||||
cv.Optional(CONF_INVERTED): cv.invalid(
|
|
||||||
"Inverted is not supported for this platform!"
|
"Inverted is not supported for this platform!"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
)
|
return _SWITCH_SCHEMA.extend(schema)
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
# Remove before 2025.11.0
|
# Remove before 2025.11.0
|
||||||
SWITCH_SCHEMA = switch_schema()
|
SWITCH_SCHEMA = switch_schema(Switch)
|
||||||
SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch"))
|
SWITCH_SCHEMA.add_extra(cv.deprecated_schema_constant("switch"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import esphome.codegen as cg
|
|||||||
from esphome.components import fan
|
from esphome.components import fan
|
||||||
from esphome.components.fan import validate_preset_modes
|
from esphome.components.fan import validate_preset_modes
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_OUTPUT_ID, CONF_PRESET_MODES, CONF_SPEED_COUNT
|
from esphome.const import CONF_PRESET_MODES, CONF_SPEED_COUNT
|
||||||
|
|
||||||
from .. import template_ns
|
from .. import template_ns
|
||||||
|
|
||||||
@ -13,21 +13,23 @@ TemplateFan = template_ns.class_("TemplateFan", cg.Component, fan.Fan)
|
|||||||
CONF_HAS_DIRECTION = "has_direction"
|
CONF_HAS_DIRECTION = "has_direction"
|
||||||
CONF_HAS_OSCILLATING = "has_oscillating"
|
CONF_HAS_OSCILLATING = "has_oscillating"
|
||||||
|
|
||||||
CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
CONFIG_SCHEMA = (
|
||||||
|
fan.fan_schema(TemplateFan)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(TemplateFan),
|
|
||||||
cv.Optional(CONF_HAS_DIRECTION, default=False): cv.boolean,
|
cv.Optional(CONF_HAS_DIRECTION, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_HAS_OSCILLATING, default=False): cv.boolean,
|
cv.Optional(CONF_HAS_OSCILLATING, default=False): cv.boolean,
|
||||||
cv.Optional(CONF_SPEED_COUNT): cv.int_range(min=1),
|
cv.Optional(CONF_SPEED_COUNT): cv.int_range(min=1),
|
||||||
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
cv.Optional(CONF_PRESET_MODES): validate_preset_modes,
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
var = await fan.new_fan(config)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await fan.register_fan(var, config)
|
|
||||||
|
|
||||||
cg.add(var.set_has_direction(config[CONF_HAS_DIRECTION]))
|
cg.add(var.set_has_direction(config[CONF_HAS_DIRECTION]))
|
||||||
cg.add(var.set_has_oscillating(config[CONF_HAS_OSCILLATING]))
|
cg.add(var.set_has_oscillating(config[CONF_HAS_OSCILLATING]))
|
||||||
|
@ -156,32 +156,24 @@ _TEXT_SENSOR_SCHEMA = (
|
|||||||
def text_sensor_schema(
|
def text_sensor_schema(
|
||||||
class_: MockObjClass = cv.UNDEFINED,
|
class_: MockObjClass = cv.UNDEFINED,
|
||||||
*,
|
*,
|
||||||
icon: str = cv.UNDEFINED,
|
|
||||||
entity_category: str = cv.UNDEFINED,
|
|
||||||
device_class: str = cv.UNDEFINED,
|
device_class: str = cv.UNDEFINED,
|
||||||
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
icon: str = cv.UNDEFINED,
|
||||||
) -> cv.Schema:
|
) -> cv.Schema:
|
||||||
schema = _TEXT_SENSOR_SCHEMA
|
schema = {}
|
||||||
|
|
||||||
if class_ is not cv.UNDEFINED:
|
if class_ is not cv.UNDEFINED:
|
||||||
schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)})
|
schema[cv.GenerateID()] = cv.declare_id(class_)
|
||||||
if icon is not cv.UNDEFINED:
|
|
||||||
schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon})
|
for key, default, validator in [
|
||||||
if device_class is not cv.UNDEFINED:
|
(CONF_ICON, icon, cv.icon),
|
||||||
schema = schema.extend(
|
(CONF_DEVICE_CLASS, device_class, validate_device_class),
|
||||||
{
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
cv.Optional(
|
]:
|
||||||
CONF_DEVICE_CLASS, default=device_class
|
if default is not cv.UNDEFINED:
|
||||||
): validate_device_class
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
}
|
|
||||||
)
|
return _TEXT_SENSOR_SCHEMA.extend(schema)
|
||||||
if entity_category is not cv.UNDEFINED:
|
|
||||||
schema = schema.extend(
|
|
||||||
{
|
|
||||||
cv.Optional(
|
|
||||||
CONF_ENTITY_CATEGORY, default=entity_category
|
|
||||||
): cv.entity_category
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return schema
|
|
||||||
|
|
||||||
|
|
||||||
# Remove before 2025.11.0
|
# Remove before 2025.11.0
|
||||||
|
@ -2077,14 +2077,20 @@ def rename_key(old_key, new_key):
|
|||||||
# Remove before 2025.11.0
|
# Remove before 2025.11.0
|
||||||
def deprecated_schema_constant(entity_type: str):
|
def deprecated_schema_constant(entity_type: str):
|
||||||
def validator(config):
|
def validator(config):
|
||||||
|
type: str = "unknown"
|
||||||
|
if (id := config.get(CONF_ID)) is not None and isinstance(id, core.ID):
|
||||||
|
type = str(id.type).split("::", maxsplit=1)[0]
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Using `%s.%s_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
|
"Using `%s.%s_SCHEMA` is deprecated and will be removed in ESPHome 2025.11.0. "
|
||||||
"Please use `%s.%s_schema(...)` instead. "
|
"Please use `%s.%s_schema(...)` instead. "
|
||||||
"If you are seeing this, report an issue to the external_component author and ask them to update it.",
|
"If you are seeing this, report an issue to the external_component author and ask them to update it. "
|
||||||
|
"https://developers.esphome.io/blog/2025/05/14/_schema-deprecations/. "
|
||||||
|
"Component using this schema: %s",
|
||||||
entity_type,
|
entity_type,
|
||||||
entity_type.upper(),
|
entity_type.upper(),
|
||||||
entity_type,
|
entity_type,
|
||||||
entity_type,
|
entity_type,
|
||||||
|
type,
|
||||||
)
|
)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Constants used by esphome."""
|
"""Constants used by esphome."""
|
||||||
|
|
||||||
__version__ = "2025.5.0-dev"
|
__version__ = "2025.6.0-dev"
|
||||||
|
|
||||||
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
ALLOWED_NAME_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||||
VALID_SUBSTITUTIONS_CHARACTERS = (
|
VALID_SUBSTITUTIONS_CHARACTERS = (
|
||||||
|
@ -13,7 +13,7 @@ platformio==6.1.18 # When updating platformio, also update /docker/Dockerfile
|
|||||||
esptool==4.8.1
|
esptool==4.8.1
|
||||||
click==8.1.7
|
click==8.1.7
|
||||||
esphome-dashboard==20250415.0
|
esphome-dashboard==20250415.0
|
||||||
aioesphomeapi==30.2.0
|
aioesphomeapi==31.0.0
|
||||||
zeroconf==0.147.0
|
zeroconf==0.147.0
|
||||||
puremagic==1.29
|
puremagic==1.29
|
||||||
ruamel.yaml==0.18.10 # dashboard_import
|
ruamel.yaml==0.18.10 # dashboard_import
|
||||||
|
@ -26,3 +26,17 @@ dfrobot_sen0395:
|
|||||||
binary_sensor:
|
binary_sensor:
|
||||||
- platform: dfrobot_sen0395
|
- platform: dfrobot_sen0395
|
||||||
id: mmwave_detected
|
id: mmwave_detected
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: dfrobot_sen0395
|
||||||
|
type: sensor_active
|
||||||
|
id: mmwave_sensor_active
|
||||||
|
- platform: dfrobot_sen0395
|
||||||
|
type: turn_on_led
|
||||||
|
id: mmwave_turn_on_led
|
||||||
|
- platform: dfrobot_sen0395
|
||||||
|
type: presence_via_uart
|
||||||
|
id: mmwave_presence_via_uart
|
||||||
|
- platform: dfrobot_sen0395
|
||||||
|
type: start_after_boot
|
||||||
|
id: mmwave_start_after_boot
|
||||||
|
38
tests/components/mipi_spi/common.yaml
Normal file
38
tests/components/mipi_spi/common.yaml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
spi:
|
||||||
|
- id: spi_single
|
||||||
|
clk_pin:
|
||||||
|
number: ${clk_pin}
|
||||||
|
allow_other_uses: true
|
||||||
|
mosi_pin:
|
||||||
|
number: ${mosi_pin}
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
spi_16: true
|
||||||
|
pixel_mode: 18bit
|
||||||
|
model: ili9488
|
||||||
|
dc_pin: ${dc_pin}
|
||||||
|
cs_pin: ${cs_pin}
|
||||||
|
reset_pin: ${reset_pin}
|
||||||
|
data_rate: 20MHz
|
||||||
|
invert_colors: true
|
||||||
|
show_test_card: true
|
||||||
|
spi_mode: mode0
|
||||||
|
draw_rounding: 8
|
||||||
|
use_axis_flips: true
|
||||||
|
init_sequence:
|
||||||
|
- [0xd0, 1, 2, 3]
|
||||||
|
- delay 10ms
|
||||||
|
transform:
|
||||||
|
swap_xy: true
|
||||||
|
mirror_x: false
|
||||||
|
mirror_y: true
|
||||||
|
dimensions:
|
||||||
|
width: 100
|
||||||
|
height: 200
|
||||||
|
enable_pin:
|
||||||
|
- number: ${clk_pin}
|
||||||
|
allow_other_uses: true
|
||||||
|
- number: ${enable_pin}
|
||||||
|
bus_mode: single
|
||||||
|
|
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: ESP32-2432S028
|
41
tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml
Normal file
41
tests/components/mipi_spi/test-jc3248w535.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: JC3248W535
|
19
tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml
Normal file
19
tests/components/mipi_spi/test-jc3636w518.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 36
|
||||||
|
data_pins:
|
||||||
|
- number: 40
|
||||||
|
- number: 41
|
||||||
|
- number: 42
|
||||||
|
- number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: JC3636W518
|
@ -0,0 +1,9 @@
|
|||||||
|
spi:
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: Pico-ResTouch-LCD-3.5
|
41
tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml
Normal file
41
tests/components/mipi_spi/test-s3box.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: S3BOX
|
41
tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml
Normal file
41
tests/components/mipi_spi/test-s3boxlite.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: S3BOXLITE
|
@ -0,0 +1,9 @@
|
|||||||
|
spi:
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-DISPLAY-S3-AMOLED-PLUS
|
@ -0,0 +1,15 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- number: 40
|
||||||
|
- number: 41
|
||||||
|
- number: 42
|
||||||
|
- number: 43
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-DISPLAY-S3-AMOLED
|
@ -0,0 +1,9 @@
|
|||||||
|
spi:
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 40
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-DISPLAY-S3-PRO
|
@ -0,0 +1,37 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-DISPLAY-S3
|
41
tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml
Normal file
41
tests/components/mipi_spi/test-t-display.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-DISPLAY
|
9
tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml
Normal file
9
tests/components/mipi_spi/test-t-embed.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
spi:
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 40
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T-EMBED
|
41
tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml
Normal file
41
tests/components/mipi_spi/test-t4-s3.esp32-s3-idf.yaml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 0
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: spi_id_3
|
||||||
|
interface: any
|
||||||
|
clk_pin: 8
|
||||||
|
mosi_pin: 9
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: T4-S3
|
@ -0,0 +1,37 @@
|
|||||||
|
spi:
|
||||||
|
- id: quad_spi
|
||||||
|
type: quad
|
||||||
|
interface: spi3
|
||||||
|
clk_pin:
|
||||||
|
number: 47
|
||||||
|
data_pins:
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
- id: octal_spi
|
||||||
|
type: octal
|
||||||
|
interface: hardware
|
||||||
|
clk_pin:
|
||||||
|
number: 9
|
||||||
|
data_pins:
|
||||||
|
- 36
|
||||||
|
- 37
|
||||||
|
- 38
|
||||||
|
- 39
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 40
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 41
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 42
|
||||||
|
- allow_other_uses: true
|
||||||
|
number: 43
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: WT32-SC01-PLUS
|
15
tests/components/mipi_spi/test.esp32-ard.yaml
Normal file
15
tests/components/mipi_spi/test.esp32-ard.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
substitutions:
|
||||||
|
clk_pin: GPIO16
|
||||||
|
mosi_pin: GPIO17
|
||||||
|
miso_pin: GPIO15
|
||||||
|
dc_pin: GPIO14
|
||||||
|
cs_pin: GPIO13
|
||||||
|
enable_pin: GPIO19
|
||||||
|
reset_pin: GPIO20
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: LANBON-L8
|
||||||
|
|
||||||
|
packages:
|
||||||
|
display: !include common.yaml
|
10
tests/components/mipi_spi/test.esp32-c3-ard.yaml
Normal file
10
tests/components/mipi_spi/test.esp32-c3-ard.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
substitutions:
|
||||||
|
clk_pin: GPIO6
|
||||||
|
mosi_pin: GPIO7
|
||||||
|
miso_pin: GPIO5
|
||||||
|
dc_pin: GPIO21
|
||||||
|
cs_pin: GPIO18
|
||||||
|
enable_pin: GPIO19
|
||||||
|
reset_pin: GPIO20
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
10
tests/components/mipi_spi/test.esp32-c3-idf.yaml
Normal file
10
tests/components/mipi_spi/test.esp32-c3-idf.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
substitutions:
|
||||||
|
clk_pin: GPIO6
|
||||||
|
mosi_pin: GPIO7
|
||||||
|
miso_pin: GPIO5
|
||||||
|
dc_pin: GPIO21
|
||||||
|
cs_pin: GPIO18
|
||||||
|
enable_pin: GPIO19
|
||||||
|
reset_pin: GPIO20
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
15
tests/components/mipi_spi/test.esp32-idf.yaml
Normal file
15
tests/components/mipi_spi/test.esp32-idf.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
substitutions:
|
||||||
|
clk_pin: GPIO16
|
||||||
|
mosi_pin: GPIO17
|
||||||
|
miso_pin: GPIO15
|
||||||
|
dc_pin: GPIO21
|
||||||
|
cs_pin: GPIO18
|
||||||
|
enable_pin: GPIO19
|
||||||
|
reset_pin: GPIO20
|
||||||
|
|
||||||
|
packages:
|
||||||
|
display: !include common.yaml
|
||||||
|
|
||||||
|
display:
|
||||||
|
- platform: mipi_spi
|
||||||
|
model: m5core
|
10
tests/components/mipi_spi/test.rp2040-ard.yaml
Normal file
10
tests/components/mipi_spi/test.rp2040-ard.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
substitutions:
|
||||||
|
clk_pin: GPIO2
|
||||||
|
mosi_pin: GPIO3
|
||||||
|
miso_pin: GPIO4
|
||||||
|
dc_pin: GPIO14
|
||||||
|
cs_pin: GPIO13
|
||||||
|
enable_pin: GPIO19
|
||||||
|
reset_pin: GPIO20
|
||||||
|
|
||||||
|
<<: !include common.yaml
|
Loading…
x
Reference in New Issue
Block a user