Compare commits

...

8 commits
temp2 ... main

Author SHA1 Message Date
Patrick Moessler
f40e05bb56 add countdown settings 2024-01-28 23:00:17 +01:00
Patrick Moessler
d8557e9cb1 improve timing 2024-01-28 22:59:40 +01:00
Patrick Moessler
208ee058fc update readme 2024-01-28 04:16:11 +01:00
Patrick Moessler
7c0a85d321 adapt yaml to qspi 2024-01-28 02:08:08 +01:00
Patrick Moessler
059dacbd06 add qspi driver 2024-01-28 00:32:51 +01:00
Patrick Moessler
eff436cb76 fix template format strings 2024-01-23 22:41:46 +01:00
Patrick Moessler
f6932ee4ec remove parameters, fix byte and bit order 2024-01-23 22:19:39 +01:00
Patrick Moessler
a2cc74c626 intermediate flexible 2024-01-23 21:33:46 +01:00
9 changed files with 727 additions and 249 deletions

View file

@ -6,14 +6,24 @@ by SN74HC595 shift registers.
## Hardware ## Hardware
Connect the shift registers like this, or adapt the pin config as needed: Connect the shift registers like this, or adapt the pin config as needed:
- Data input (DIO) to ESP GPIO19 - Data input (DIO) to ESP:
- Shift clock input (SCK) to ESP GPIO18 - GPIO23 (first display),
- Latch / register clock input (RCK) to ESP GPIO5 - GPIO19 (second display),
- GPIO22 (third display),
- GPIO21 (fourth display)
- Shift clock input (SCK) to ESP GPIO18 (all displays)
- Latch / register clock input (RCK) to ESP GPIO5 (all display)
Keep in mind that the ESP32 is a 3.3V device when connecting to a 5V display. Keep in mind that the ESP32 is a 3.3V device when connecting to a 5V display. The shift registers should handle the 3.3V
signals from the ESP, annd in some cases, 3.3V can also be used to power the displays at a lower brightness.
## Setup ## Setup
> Note: the QSPI driver in esphome is (as of 2024-01-28) not yet in the released package.
> Install a suitable version directly from github with:
> `pip install "esphome @ git+https://github.com/esphome/esphome@1fef769496ed89c0062d8e70f5964b8318ba4550"`
Set the board type in the YAML (default should also work on most ESP32 devices) Set the board type in the YAML (default should also work on most ESP32 devices)
Adapt the config as needed for the fallback AP and if wanted, api key. Adapt the config as needed for the fallback AP and if wanted, api key.
Create a `secrets.yaml` containing this: Create a `secrets.yaml` containing this:
@ -35,4 +45,4 @@ The driver in `components/` is directly based on the existing MAX7219 driver in
subject to the GPLv3 and MIT licenses, as outlined in the LICENSE document (also copied from ESPHome). subject to the GPLv3 and MIT licenses, as outlined in the LICENSE document (also copied from ESPHome).
## Open Issues ## Open Issues
The actual segment mapping is not tested well, due to missing hardware.

View file

@ -0,0 +1,34 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import display, spi
from esphome.const import CONF_ID, CONF_INTENSITY, CONF_LAMBDA, CONF_NUM_CHIPS
DEPENDENCIES = ["spi"]
qspi_74hc595_4x_display_ns = cg.esphome_ns.namespace("qspi_74hc595_4x_display")
QSPI_74HC595_4X_DISPLAYComponent = qspi_74hc595_4x_display_ns.class_(
"QSPI_74HC595_4X_DISPLAYComponent", cg.PollingComponent, spi.SPIDevice
)
QSPI_74HC595_4X_DISPLAYComponentRef = QSPI_74HC595_4X_DISPLAYComponent.operator("ref")
CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(QSPI_74HC595_4X_DISPLAYComponent),
}
)
.extend(cv.polling_component_schema("1s"))
.extend(spi.spi_device_schema(quad=True))
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await spi.register_spi_device(var, config)
await display.register_display(var, config)
if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(
config[CONF_LAMBDA], [(QSPI_74HC595_4X_DISPLAYComponentRef, "it")], return_type=cg.void
)
cg.add(var.set_writer(lambda_))

View file

@ -0,0 +1,265 @@
#include "qspi_74hc595_4x_display.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome
{
namespace qspi_74hc595_4x_display
{
static const char *const TAG = "qspi_74hc595_4x_display";
static const uint32_t QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR = 0x11111111;
static const uint32_t QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW[95] = {
// BADCFE.G
// 0xBADCFE.G
0x00000000, // ' ', ord 0x20
0x10010010, // '!', ord 0x21
0x10001000, // '"', ord 0x22
0x10011100, // '#', ord 0x23
0x01111000, // '$', ord 0x24
0x01100001, // '%', ord 0x25
0x11100100, // '&', ord 0x26
0x00001000, // ''', ord 0x27
0x01101100, // '(', ord 0x28
0x11110000, // ')', ord 0x29
0x01000000, // '*', ord 0x2A
0x00000100, // '+', ord 0x2B
0x00010000, // ',', ord 0x2C
0x00000001, // '-', ord 0x2D
0x00000010, // '.', ord 0x2E
0x10000101, // '/', ord 0x2F
0x11111100, // '0', ord 0x30
0x10010000, // '1', ord 0x31
0x11100101, // '2', ord 0x32
0x11110001, // '3', ord 0x33
0x10011001, // '4', ord 0x34
0x01111001, // '5', ord 0x35
0x01111101, // '6', ord 0x36
0x11010000, // '7', ord 0x37
0x11111101, // '8', ord 0x38
0x11111001, // '9', ord 0x39
0x01100000, // ':', ord 0x3A
0x01110000, // ';', ord 0x3B
0x00100100, // '<', ord 0x3C
0x00100001, // '=', ord 0x3D
0x00110000, // '>', ord 0x3E
0x11000101, // '?', ord 0x3F
0x11101101, // '@', ord 0x40
0x11011101, // 'A', ord 0x41
0x00111101, // 'B', ord 0x42
0x01101100, // 'C', ord 0x43
0x10110101, // 'D', ord 0x44
0x01101101, // 'E', ord 0x45
0x01001101, // 'F', ord 0x46
0x01111100, // 'G', ord 0x47
0x10011101, // 'H', ord 0x48
0x10010000, // 'I', ord 0x49
0x10110100, // 'J', ord 0x4A
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'K', ord 0x4B
0x00101100, // 'L', ord 0x4C
0x11011100, // 'M', ord 0x4D
0x00010101, // 'N', ord 0x4E
0x11111100, // 'O', ord 0x4F
0x11001101, // 'P', ord 0x50
0x11111110, // 'Q', ord 0x51
0x00000101, // 'R', ord 0x52
0x01111001, // 'S', ord 0x53
0x00001101, // 'T', ord 0x54
0x10111100, // 'U', ord 0x55
0x10111100, // 'V', ord 0x56
0x10111101, // 'W', ord 0x57
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'X', ord 0x58
0x10001101, // 'Y', ord 0x59
0x11100101, // 'Z', ord 0x5A
0x01101100, // '[', ord 0x5B
0x00011001, // '\', ord 0x5C
0x11110000, // ']', ord 0x5D
0x11001000, // '^', ord 0x5E
0x00100000, // '_', ord 0x5F
0x10000000, // '`', ord 0x60
0x11011101, // 'a', ord 0x61
0x00111101, // 'b', ord 0x62
0x00100101, // 'c', ord 0x63
0x10110101, // 'd', ord 0x64
0x01101101, // 'e', ord 0x65
0x01001101, // 'f', ord 0x66
0x01111100, // 'g', ord 0x67
0x00011101, // 'h', ord 0x68
0x00010000, // 'i', ord 0x69
0x10110100, // 'j', ord 0x6A
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'k', ord 0x6B
0x00101100, // 'l', ord 0x6C
0x11011100, // 'm', ord 0x6D
0x00010101, // 'n', ord 0x6E
0x00110101, // 'o', ord 0x6F
0x11001101, // 'p', ord 0x70
0x11011001, // 'q', ord 0x71
0x00000101, // 'r', ord 0x72
0x01111001, // 's', ord 0x73
0x00001101, // 't', ord 0x74
0x00110100, // 'u', ord 0x75
0x00110100, // 'v', ord 0x76
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'w', ord 0x77
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'x', ord 0x78
0x10001101, // 'y', ord 0x79
QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR, // 'z', ord 0x7A
0x10010001, // '{', ord 0x7B
0x00001100, // '|', ord 0x7C
0x00001101, // '}', ord 0x7D
0x11001001, // '~', ord 0x7E (degree symbol)
};
static constexpr uint8_t QSPI_74HC595_4X_DISPLAY_DIGIT_MAP[8] = {2, 3, 0, 1, 6, 7, 4, 5};
static constexpr uint8_t QSPI_74HC595_4X_DISPLAY_ZEROS[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
float QSPI_74HC595_4X_DISPLAYComponent::get_setup_priority() const
{
return setup_priority::PROCESSOR;
}
void QSPI_74HC595_4X_DISPLAYComponent::setup()
{
ESP_LOGCONFIG(TAG, "Setting up QSPI_74HC595_4X_DISPLAY...");
this->spi_setup();
memset(this->buffer_, 0xff, sizeof(this->buffer_));
// this->buffer_[0*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['.'-' '];
// this->buffer_[1*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['*'-' '];
// this->buffer_[2*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['`'-' '];
// this->buffer_[3*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW[','-' '];
// this->buffer_[4*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['_'-' '];
// this->buffer_[5*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['+'-' '];
// this->buffer_[6*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['\''-' '];
// this->buffer_[7*2] = ~QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW['-'-' '];
for (uint8_t i = 0; i < 8; i++)
{
this->buffer_[(i * 2) + 1] = 0xF << (QSPI_74HC595_4X_DISPLAY_DIGIT_MAP[i] * 4);
}
}
void QSPI_74HC595_4X_DISPLAYComponent::dump_config()
{
ESP_LOGCONFIG(TAG, "QSPI_74HC595_4X_DISPLAY:");
LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this);
}
void QSPI_74HC595_4X_DISPLAYComponent::display()
{
uint32_t delay = static_cast<uint64_t>(this->get_update_interval()) * 1000 / 8;
const uint8_t *buf_ptr = reinterpret_cast<const uint8_t *>(this->buffer_);
for (uint8_t i = 0; i < 8; i++)
{
uint32_t start = micros();
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, buf_ptr, 8, 4);
buf_ptr += 8;
this->disable();
delay_microseconds_safe(delay - (micros() - start));
}
// zero out everything to have a somewhat uniform duty cycle for all digits
this->enable();
this->write_cmd_addr_data(0, 0, 0, 0, QSPI_74HC595_4X_DISPLAY_ZEROS, 32, 4);
this->disable();
}
void QSPI_74HC595_4X_DISPLAYComponent::update()
{
for (uint8_t i = 0; i < 8; i++)
this->buffer_[i * 2] = 0xFFFFFFFF;
if (this->writer_.has_value())
(*this->writer_)(*this);
this->display();
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::print(uint8_t start_pos, const char *str)
{
uint8_t pos = start_pos;
for (; *str != '\0'; str++)
{
uint32_t data = QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR;
if (*str >= ' ' && *str <= '~')
data = QSPI_74HC595_4X_DISPLAY_ASCII_TO_RAW[*str - ' '];
if (data == QSPI_74HC595_4X_DISPLAY_UNKNOWN_CHAR)
{
ESP_LOGW(
TAG,
"Encountered character '%c' with no QSPI_74HC595_4X_DISPLAY representation while translating string!",
*str);
}
if (*str == '.')
{
if (pos != start_pos)
pos--;
this->buffer_[(pos % 8) * 2] &= ~(0x00000010 << (pos / 8));
}
else
{
if (pos >= 32)
{
ESP_LOGE(TAG, "QSPI_74HC595_4X_DISPLAY String is too long for the display!");
break;
}
this->buffer_[(pos % 8) * 2] &= ~(data << (pos / 8));
}
pos++;
}
return pos - start_pos;
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::print(const char *str)
{
return this->print(0, str);
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::printf(uint8_t pos, const char *format, ...)
{
va_list arg;
va_start(arg, format);
char buffer[64];
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
va_end(arg);
if (ret > 0)
return this->print(pos, buffer);
return 0;
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::printf(const char *format, ...)
{
va_list arg;
va_start(arg, format);
char buffer[64];
int ret = vsnprintf(buffer, sizeof(buffer), format, arg);
va_end(arg);
if (ret > 0)
return this->print(buffer);
return 0;
}
void QSPI_74HC595_4X_DISPLAYComponent::set_writer(qspi_74hc595_4x_display_writer_t &&writer)
{
this->writer_ = writer;
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::strftime(uint8_t pos, const char *format, ESPTime time)
{
char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0)
return this->print(pos, buffer);
return 0;
}
uint8_t QSPI_74HC595_4X_DISPLAYComponent::strftime(const char *format, ESPTime time)
{
return this->strftime(0, format, time);
}
} // namespace qspi_74hc595_4x_display
} // namespace esphome

View file

@ -0,0 +1,56 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/time.h"
#include "esphome/components/spi/spi.h"
namespace esphome {
namespace qspi_74hc595_4x_display {
class QSPI_74HC595_4X_DISPLAYComponent;
using qspi_74hc595_4x_display_writer_t = std::function<void(QSPI_74HC595_4X_DISPLAYComponent &)>;
class QSPI_74HC595_4X_DISPLAYComponent : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_10MHZ> {
public:
void set_writer(qspi_74hc595_4x_display_writer_t &&writer);
void setup() override;
void dump_config() override;
void update() override;
float get_setup_priority() const override;
void display();
/// Evaluate the printf-format and print the result at the given position.
uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4)));
/// Evaluate the printf-format and print the result at position 0.
uint8_t printf(const char *format, ...) __attribute__((format(printf, 2, 3)));
/// Print `str` at the given position.
uint8_t print(uint8_t pos, const char *str);
/// Print `str` at position 0.
uint8_t print(const char *str);
/// Evaluate the strftime-format and print the result at the given position.
uint8_t strftime(uint8_t pos, const char *format, ESPTime time) __attribute__((format(strftime, 3, 0)));
/// Evaluate the strftime-format and print the result at position 0.
uint8_t strftime(const char *format, ESPTime time) __attribute__((format(strftime, 2, 0)));
protected:
void send_byte_(uint8_t a_register, uint8_t data);
void send_to_all_(uint8_t a_register, uint8_t data);
uint32_t buffer_[16]; // 8* segment+digit words
optional<qspi_74hc595_4x_display_writer_t> writer_{};
};
} // namespace qspi_74hc595_4x_display
} // namespace esphome

View file

@ -12,8 +12,6 @@ SPI_74HC595_DISPLAYComponent = spi_74hc595_display_ns.class_(
SPI_74HC595_DISPLAYComponentRef = SPI_74HC595_DISPLAYComponent.operator("ref") SPI_74HC595_DISPLAYComponentRef = SPI_74HC595_DISPLAYComponent.operator("ref")
CONF_REVERSE = "reverse" CONF_REVERSE = "reverse"
CONF_SEGMENT_FIRST = "segment_first"
CONF_COMMON_CATHODE = "common_cathode"
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
display.BASIC_DISPLAY_SCHEMA.extend( display.BASIC_DISPLAY_SCHEMA.extend(
@ -21,8 +19,6 @@ CONFIG_SCHEMA = (
cv.GenerateID(): cv.declare_id(SPI_74HC595_DISPLAYComponent), cv.GenerateID(): cv.declare_id(SPI_74HC595_DISPLAYComponent),
cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255), cv.Optional(CONF_NUM_CHIPS, default=1): cv.int_range(min=1, max=255),
cv.Optional(CONF_REVERSE, default=False): cv.boolean, cv.Optional(CONF_REVERSE, default=False): cv.boolean,
cv.Optional(CONF_SEGMENT_FIRST, default=False): cv.boolean,
cv.Optional(CONF_COMMON_CATHODE, default=False): cv.boolean,
} }
) )
.extend(cv.polling_component_schema("1s")) .extend(cv.polling_component_schema("1s"))
@ -37,8 +33,6 @@ async def to_code(config):
cg.add(var.set_num_chips(config[CONF_NUM_CHIPS])) cg.add(var.set_num_chips(config[CONF_NUM_CHIPS]))
cg.add(var.set_reverse(config[CONF_REVERSE])) cg.add(var.set_reverse(config[CONF_REVERSE]))
cg.add(var.set_segment_first(config[CONF_SEGMENT_FIRST]))
cg.add(var.set_common_cathode(config[CONF_COMMON_CATHODE]))
if CONF_LAMBDA in config: if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda( lambda_ = await cg.process_lambda(

View file

@ -1,10 +1,12 @@
#include "spi_74hc595_display.h" #include "spi_74hc595_display.h"
#include "esphome/core/log.h"
#include "esphome/core/helpers.h"
#include "esphome/core/hal.h" #include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
namespace esphome { namespace esphome
namespace spi_74hc595_display { {
namespace spi_74hc595_display
{
static const char *const TAG = "spi_74hc595_display"; static const char *const TAG = "spi_74hc595_display";
@ -109,34 +111,49 @@ const uint8_t SPI_74HC595_DISPLAY_ASCII_TO_RAW[95] PROGMEM = {
0b01100011, // '~', ord 0x7E (degree symbol) 0b01100011, // '~', ord 0x7E (degree symbol)
}; };
float SPI_74HC595_DISPLAYComponent::get_setup_priority() const { return setup_priority::PROCESSOR; } const uint8_t SPI_74HC595_DISPLAY_DIGIT_MASK[8] PROGMEM = {
(1U << 4), (1U << 5), (1U << 6), (1U << 7), (1U << 0), (1U << 1), (1U << 2), (1U << 3),
};
void SPI_74HC595_DISPLAYComponent::setup() { float SPI_74HC595_DISPLAYComponent::get_setup_priority() const
{
return setup_priority::PROCESSOR;
}
void SPI_74HC595_DISPLAYComponent::setup()
{
ESP_LOGCONFIG(TAG, "Setting up SPI_74HC595_DISPLAY..."); ESP_LOGCONFIG(TAG, "Setting up SPI_74HC595_DISPLAY...");
this->spi_setup(); this->spi_setup();
this->buffer_ = new uint8_t[this->num_chips_ * 8]; // NOLINT this->buffer_ = new uint8_t[this->num_chips_ * 8]; // NOLINT
for (uint8_t i = 0; i < this->num_chips_ * 8; i++) for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
{
this->buffer_[i] = 0; this->buffer_[i] = 0;
} }
}
void SPI_74HC595_DISPLAYComponent::dump_config() { void SPI_74HC595_DISPLAYComponent::dump_config()
{
ESP_LOGCONFIG(TAG, "SPI_74HC595_DISPLAY:"); ESP_LOGCONFIG(TAG, "SPI_74HC595_DISPLAY:");
ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_); ESP_LOGCONFIG(TAG, " Number of Chips: %u", this->num_chips_);
LOG_PIN(" CS Pin: ", this->cs_); LOG_PIN(" CS Pin: ", this->cs_);
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
} }
void SPI_74HC595_DISPLAYComponent::display() { void SPI_74HC595_DISPLAYComponent::display()
{
uint32_t delay = static_cast<uint64_t>(this->get_update_interval()) * 1000 / 8; uint32_t delay = static_cast<uint64_t>(this->get_update_interval()) * 1000 / 8;
uint8_t flip_digit=(this->common_cathode_)?0xFF:0x00; for (uint8_t i = 0; i < 8; i++)
uint8_t flip_segment = ~flip_digit; {
for (uint8_t i = 0; i < 8; i++) {
this->enable(); this->enable();
for (uint8_t j = 0; j < this->num_chips_; j++) { for (uint8_t j = 0; j < this->num_chips_; j++)
if (reverse_) { {
this->send_byte_((1U << i)^flip_digit, buffer_[(num_chips_ - j - 1) * 8 + i]^flip_segment); if (reverse_)
} else { {
this->send_byte_((1U << i)^flip_digit, (buffer_[j * 8 + i])^flip_segment); this->send_byte_(SPI_74HC595_DISPLAY_DIGIT_MASK[i], ~buffer_[(num_chips_ - j - 1) * 8 + i]);
}
else
{
this->send_byte_(SPI_74HC595_DISPLAY_DIGIT_MASK[i], ~buffer_[j * 8 + i]);
} }
} }
this->disable(); this->disable();
@ -144,23 +161,21 @@ void SPI_74HC595_DISPLAYComponent::display() {
} }
// zero out everything to have a somewhat uniform duty cycle for all digits // zero out everything to have a somewhat uniform duty cycle for all digits
this->enable(); this->enable();
for (uint8_t j = 0; j < this->num_chips_; j++) { for (uint8_t j = 0; j < this->num_chips_; j++)
{
this->send_byte_(0, 0); this->send_byte_(0, 0);
} }
this->disable(); this->disable();
} }
void SPI_74HC595_DISPLAYComponent::send_byte_(uint8_t a_digit, uint8_t data) { void SPI_74HC595_DISPLAYComponent::send_byte_(uint8_t a_digit, uint8_t data)
if(this->segment_first_){ {
this->write_byte(data); this->write_byte(data);
this->write_byte(a_digit); this->write_byte(a_digit);
} else {
this->write_byte(a_digit);
this->write_byte(data);
}
} }
void SPI_74HC595_DISPLAYComponent::update() { void SPI_74HC595_DISPLAYComponent::update()
{
for (uint8_t i = 0; i < this->num_chips_ * 8; i++) for (uint8_t i = 0; i < this->num_chips_ * 8; i++)
this->buffer_[i] = 0; this->buffer_[i] = 0;
if (this->writer_.has_value()) if (this->writer_.has_value())
@ -168,22 +183,31 @@ void SPI_74HC595_DISPLAYComponent::update() {
this->display(); this->display();
} }
uint8_t SPI_74HC595_DISPLAYComponent::print(uint8_t start_pos, const char *str) { uint8_t SPI_74HC595_DISPLAYComponent::print(uint8_t start_pos, const char *str)
{
uint8_t pos = start_pos; uint8_t pos = start_pos;
for (; *str != '\0'; str++) { for (; *str != '\0'; str++)
{
uint8_t data = SPI_74HC595_DISPLAY_UNKNOWN_CHAR; uint8_t data = SPI_74HC595_DISPLAY_UNKNOWN_CHAR;
if (*str >= ' ' && *str <= '~') if (*str >= ' ' && *str <= '~')
data = progmem_read_byte(&SPI_74HC595_DISPLAY_ASCII_TO_RAW[*str - ' ']); data = progmem_read_byte(&SPI_74HC595_DISPLAY_ASCII_TO_RAW[*str - ' ']);
if (data == SPI_74HC595_DISPLAY_UNKNOWN_CHAR) { if (data == SPI_74HC595_DISPLAY_UNKNOWN_CHAR)
ESP_LOGW(TAG, "Encountered character '%c' with no SPI_74HC595_DISPLAY representation while translating string!", *str); {
ESP_LOGW(TAG,
"Encountered character '%c' with no SPI_74HC595_DISPLAY representation while translating string!",
*str);
} }
if (*str == '.') { if (*str == '.')
{
if (pos != start_pos) if (pos != start_pos)
pos--; pos--;
this->buffer_[pos] |= 0b10000000; this->buffer_[pos] |= 0b10000000;
} else { }
if (pos >= this->num_chips_ * 8) { else
{
if (pos >= this->num_chips_ * 8)
{
ESP_LOGE(TAG, "SPI_74HC595_DISPLAY String is too long for the display!"); ESP_LOGE(TAG, "SPI_74HC595_DISPLAY String is too long for the display!");
break; break;
} }
@ -194,9 +218,13 @@ uint8_t SPI_74HC595_DISPLAYComponent::print(uint8_t start_pos, const char *str)
return pos - start_pos; return pos - start_pos;
} }
uint8_t SPI_74HC595_DISPLAYComponent::print(const char *str) { return this->print(0, str); } uint8_t SPI_74HC595_DISPLAYComponent::print(const char *str)
{
return this->print(0, str);
}
uint8_t SPI_74HC595_DISPLAYComponent::printf(uint8_t pos, const char *format, ...) { uint8_t SPI_74HC595_DISPLAYComponent::printf(uint8_t pos, const char *format, ...)
{
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
char buffer[64]; char buffer[64];
@ -207,7 +235,8 @@ uint8_t SPI_74HC595_DISPLAYComponent::printf(uint8_t pos, const char *format, ..
return 0; return 0;
} }
uint8_t SPI_74HC595_DISPLAYComponent::printf(const char *format, ...) { uint8_t SPI_74HC595_DISPLAYComponent::printf(const char *format, ...)
{
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
char buffer[64]; char buffer[64];
@ -218,18 +247,28 @@ uint8_t SPI_74HC595_DISPLAYComponent::printf(const char *format, ...) {
return 0; return 0;
} }
void SPI_74HC595_DISPLAYComponent::set_writer(spi_74hc595_display_writer_t &&writer) { this->writer_ = writer; } void SPI_74HC595_DISPLAYComponent::set_writer(spi_74hc595_display_writer_t &&writer)
{
this->writer_ = writer;
}
void SPI_74HC595_DISPLAYComponent::set_num_chips(uint8_t num_chips) { this->num_chips_ = num_chips; } void SPI_74HC595_DISPLAYComponent::set_num_chips(uint8_t num_chips)
{
this->num_chips_ = num_chips;
}
uint8_t SPI_74HC595_DISPLAYComponent::strftime(uint8_t pos, const char *format, ESPTime time) { uint8_t SPI_74HC595_DISPLAYComponent::strftime(uint8_t pos, const char *format, ESPTime time)
{
char buffer[64]; char buffer[64];
size_t ret = time.strftime(buffer, sizeof(buffer), format); size_t ret = time.strftime(buffer, sizeof(buffer), format);
if (ret > 0) if (ret > 0)
return this->print(pos, buffer); return this->print(pos, buffer);
return 0; return 0;
} }
uint8_t SPI_74HC595_DISPLAYComponent::strftime(const char *format, ESPTime time) { return this->strftime(0, format, time); } uint8_t SPI_74HC595_DISPLAYComponent::strftime(const char *format, ESPTime time)
{
return this->strftime(0, format, time);
}
} // namespace spi_74hc595_display } // namespace spi_74hc595_display
} // namespace esphome } // namespace esphome

View file

@ -30,8 +30,6 @@ class SPI_74HC595_DISPLAYComponent : public PollingComponent,
void set_num_chips(uint8_t num_chips); void set_num_chips(uint8_t num_chips);
void set_reverse(bool reverse) { this->reverse_ = reverse; }; void set_reverse(bool reverse) { this->reverse_ = reverse; };
void set_segment_first(bool segment_first) { this->segment_first_ = segment_first; };
void set_common_cathode(bool common_cathode) { this->common_cathode_ = common_cathode; };
/// Evaluate the printf-format and print the result at the given position. /// Evaluate the printf-format and print the result at the given position.
uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4))); uint8_t printf(uint8_t pos, const char *format, ...) __attribute__((format(printf, 3, 4)));
@ -56,8 +54,6 @@ class SPI_74HC595_DISPLAYComponent : public PollingComponent,
uint8_t num_chips_{1}; uint8_t num_chips_{1};
uint8_t *buffer_; uint8_t *buffer_;
bool reverse_{false}; bool reverse_{false};
bool segment_first_{false};
bool common_cathode_{false};
optional<spi_74hc595_display_writer_t> writer_{}; optional<spi_74hc595_display_writer_t> writer_{};
}; };

View file

@ -5,7 +5,7 @@ esphome:
esp32: esp32:
board: esp32dev board: esp32dev
framework: framework:
type: arduino type: esp-idf
# Enable logging # Enable logging
logger: logger:
@ -44,63 +44,147 @@ time:
- platform: sntp - platform: sntp
id: sntp_time id: sntp_time
timezone: Europe/Berlin timezone: Europe/Berlin
on_time:
- seconds: /10
then:
- logger.log:
level: INFO
format: "%2d %2d %2d %2d"
args:
- 'uint32_t(id(runtime).state/3600) - uint32_t(id(runtime).state/86400)*1440'
- 'uint32_t(id(runtime).state/60) - uint32_t(id(runtime).state/3600)*60'
- 'uint32_t(id(runtime).state) - uint32_t(id(runtime).state/60)*60'
- 'uint32_t((id(runtime).state - uint32_t(id(runtime).state))*25)'
spi: spi:
clk_pin: GPIO18 - id: quad_spi_bus
mosi_pin: GPIO19 interface: spi3
clk_pin: 18
data_pins:
- 23
- 19
- 22
- 21
sensor: sensor:
- platform: uptime - platform: uptime
id: runtime id: runtime
update_interval: 100ms update_interval: 100ms
globals:
- id: countdown_end
type: time_t
- id: uptime_start
type: time_t
number:
- platform: template
name: "Countdown set H"
id: cd_set_h
initial_value: 0
min_value: 0
max_value: 99
step: 1
optimistic: true
mode: box
- platform: template
name: "Countdown set M"
id: cd_set_m
initial_value: 0
min_value: 0
max_value: 59
step: 1
optimistic: true
mode: box
- platform: template
name: "Countdown set S"
id: cd_set_s
initial_value: 0
min_value: 0
max_value: 59
step: 1
optimistic: true
mode: box
button:
- platform: template
name: "Countdown set"
on_press:
- lambda: |-
id(countdown_end) =
id(sntp_time).timestamp_now()
+ uint32_t(id(cd_set_h).state)*3600
+ uint32_t(id(cd_set_m).state)*60
+ uint32_t(id(cd_set_s).state);
- platform: template
name: "Countdown Minutes 1m"
on_press:
- lambda: |-
id(countdown_end) = id(sntp_time).timestamp_now() + 60;
- platform: template
name: "Countdown Minutes 5m"
on_press:
- lambda: |-
id(countdown_end) = id(sntp_time).timestamp_now() + 300;
- platform: template
name: "Countdown Minutes 10m"
on_press:
- lambda: |-
id(countdown_end) = id(sntp_time).timestamp_now() + 600;
- platform: template
name: "Countdown Hours 1h"
on_press:
- lambda: |-
id(countdown_end) = id(sntp_time).timestamp_now() + 3600;
- platform: template
name: "Countdown Hours 2h"
on_press:
- lambda: |-
id(countdown_end) = id(sntp_time).timestamp_now() + 7200;
- platform: template
name: "Runtime Reset"
on_press:
- lambda: |-
id(uptime_start) = id(runtime).state;
display: display:
- platform: spi_74hc595_display - platform: qspi_74hc595_4x_display
cs_pin: GPIO5 cs_pin: GPIO5
data_rate: 1MHz #10MHz data_rate: 10MHz
num_chips: 1 #3 update_interval: 10ms
update_interval: 4s #16ms spi_id: quad_spi_bus
common_cathode: false
segment_first: false
reverse: false
lambda: |- lambda: |-
static uint32_t last_micros=0;
static uint32_t clock_frames=0; static uint32_t clock_frames=0;
static uint32_t old_clock=0; static uint32_t old_clock=0;
if(id(sntp_time).now().second!=old_clock){ if(id(sntp_time).now().second!=old_clock){
clock_frames=0; last_micros = micros();
old_clock=id(sntp_time).now().second; old_clock=id(sntp_time).now().second;
} else { } else {
clock_frames++; clock_frames = (micros()-last_micros)/10000;
} }
//it.printf(0, ".*`,_+'-"); /////////// first display
it.printf(0, "88888888"); it.printf(0, ".*`,_+'-");
//it.strftime(0, "%H%M%s", id(sntp_time).now());
//it.printf(6, "%2d", clock_frames);
//it.strftime(8, "%H%M%s", id(sntp_time).now()); /////////// second display
//it.printf(14, "%2d", clock_frames); it.strftime(8, "%H%M%S", id(sntp_time).now());
/*it.strftime(8, "%H%M%s", id(sntp_time).now());*/ it.printf(14, "%02d", clock_frames%100);
/*it.printf(14, "%1d", 0)*/
/*it.printf(16, "%02d%02d%02d%02d", /////////// third display
uint32_t(id(runtime).state/3600) - uint32_t(id(runtime).state/86400)*1440, {
uint32_t(id(runtime).state/60) - uint32_t(id(runtime).state/3600)*60, double delta = id(runtime).state - id(uptime_start);
uint32_t(id(runtime).state) - uint32_t(id(runtime).state/60)*60, uint32_t hours = delta/3600;
uint32_t((id(runtime).state - uint32_t(id(runtime).state))*25) ); delta -= hours*3600;
*/ uint32_t minutes = delta/60;
delta -= minutes*60;
uint32_t seconds = delta;
uint32_t frames = (100*delta) - (100*seconds);
it.printf(16, "%02d%02d%02d%02d", hours, minutes, seconds, frames);
}
/////////// fourth display
if (id(countdown_end) > id(sntp_time).timestamp_now()) {
time_t delta=difftime(id(countdown_end), id(sntp_time).timestamp_now());
time_t hours = delta/3600;
delta -= hours*3600;
time_t minutes = (delta)/60;
delta -= minutes*60;
time_t seconds = delta;
it.printf(24, "%02ld%02ld%02ld%02d", hours, minutes, seconds, (99-clock_frames%100));
}
# the first display should display a test string to figure out the digit and segment map: # the first display should display a test string to figure out the digit and segment map:
# digit 0: >.< character (dot segment on) # digit 0: >.< character (dot segment on)