mirror of
https://github.com/sascha-hemi/esphome-ikea-led-matrix.git
synced 2026-03-21 04:06:00 +01:00
add Obegränsad component; update README
This commit is contained in:
95
README.md
95
README.md
@@ -1,6 +1,6 @@
|
||||
# esphome-frekvens-component
|
||||
# esphome IKEA LED matrix components for Frekvens & Obegränsad
|
||||
|
||||
A custom component for esp-home to interface with IKEA's Frekvens Cube.
|
||||
A custom component for esphome to interface with IKEA's Frekvens Cube and Obegränsad panel.
|
||||
|
||||
## Sources
|
||||
|
||||
@@ -15,26 +15,90 @@ Currently there is a dependency upon Adafruit GFX library. In your esphome confi
|
||||
- Declare necessary libraries
|
||||
- Define frekvenspanel component
|
||||
|
||||
Here is a short config to demonstrate the usage to display time on panel:
|
||||
```yaml
|
||||
lib_deps:
|
||||
- Wire # Also required by GFX.
|
||||
- SPI # Also required by GFX.
|
||||
- adafruit/Adafruit BusIO # Required by GFX Library.
|
||||
- adafruit/Adafruit GFX Library # Required for FrekvensPanel.
|
||||
- me-no-dev/ESPAsyncTCP
|
||||
```
|
||||
|
||||
Here is a short config to demonstrate the usage to display time on Frekvens and Obegränsad:
|
||||
|
||||
### Frekvens
|
||||
```yaml
|
||||
esphome:
|
||||
name: frekvens8266-weather
|
||||
name: frekvens-clock
|
||||
platform: ESP8266
|
||||
board: d1_mini
|
||||
platformio_options:
|
||||
upload_speed: 115200
|
||||
lib_deps:
|
||||
- me-no-dev/ESPAsyncTCP
|
||||
- adafruit/Adafruit GFX Library # Required for FrekvensPanel.
|
||||
- Adafruit BusIO # Required by GFX Library.
|
||||
- Wire # Also required by GFX.
|
||||
- SPI # Also required by GFX.
|
||||
- adafruit/Adafruit BusIO # Required by GFX Library.
|
||||
- adafruit/Adafruit GFX Library # Required for FrekvensPanel.
|
||||
- me-no-dev/ESPAsyncTCP
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: <path of directory containing frekvens_panel>
|
||||
- source: github://phiten/esphome-ikea-led-matrix@master
|
||||
components: [ frekvens_panel ]
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
name: 'Brightness'
|
||||
output: matrix_brightness
|
||||
restore_mode: RESTORE_DEFAULT_ON
|
||||
|
||||
output:
|
||||
- platform: esp8266_pwm
|
||||
# Enables brightness control.
|
||||
max_power: 0.05
|
||||
id: matrix_brightness
|
||||
pin:
|
||||
number: GPIO14
|
||||
inverted: True
|
||||
|
||||
time:
|
||||
- platform: sntp
|
||||
id: ntp_time
|
||||
timezone: 'Europe/Berlin'
|
||||
|
||||
font:
|
||||
- file: "04B03.ttf"
|
||||
id: b03
|
||||
size: 8
|
||||
|
||||
display:
|
||||
- platform: frekvens_panel
|
||||
rotation: 90
|
||||
latch_pin: 12
|
||||
clock_pin: 04
|
||||
data_pin: 05
|
||||
|
||||
lambda: |-
|
||||
it.strftime(4, 0, id(b03), "%H", id(ntp_time).now());
|
||||
it.strftime(4, 8, id(b03), "%M", id(ntp_time).now());
|
||||
|
||||
```
|
||||
|
||||
### Obegränsad
|
||||
```yaml
|
||||
esphome:
|
||||
name: obegraensad-clock
|
||||
platform: ESP8266
|
||||
board: d1_mini
|
||||
platformio_options:
|
||||
lib_deps:
|
||||
- Wire # Also required by GFX.
|
||||
- SPI # Also required by GFX.
|
||||
- adafruit/Adafruit BusIO # Required by GFX Library.
|
||||
- adafruit/Adafruit GFX Library # Required for FrekvensPanel.
|
||||
- me-no-dev/ESPAsyncTCP
|
||||
|
||||
external_components:
|
||||
- source: github://phiten/esphome-ikea-led-matrix@master
|
||||
components: [ obegraensad_panel ]
|
||||
|
||||
light:
|
||||
- platform: monochromatic
|
||||
@@ -53,7 +117,7 @@ output:
|
||||
time:
|
||||
- platform: sntp
|
||||
id: ntp_time
|
||||
timezone: 'Europe/Paris'
|
||||
timezone: 'Europe/Berlin'
|
||||
|
||||
font:
|
||||
- file: "04B03.ttf"
|
||||
@@ -61,14 +125,15 @@ font:
|
||||
size: 8
|
||||
|
||||
display:
|
||||
- platform: frekvens_panel
|
||||
- platform: obegraensad_panel
|
||||
rotation: 90
|
||||
latch_pin: 12
|
||||
clock_pin: 04
|
||||
data_pin: 05
|
||||
|
||||
lambda: |-
|
||||
auto time = id(ntp_time).now();
|
||||
it.printf(0, 0, id(b03), "%d:%d", time.hour, time.minute);
|
||||
it.strftime(4, 0, id(b03), "%H", id(ntp_time).now());
|
||||
it.strftime(4, 8, id(b03), "%M", id(ntp_time).now());
|
||||
|
||||
```
|
||||
|
||||
|
||||
0
components/obegraensad_panel/__init__.py
Normal file
0
components/obegraensad_panel/__init__.py
Normal file
54
components/obegraensad_panel/display.py
Normal file
54
components/obegraensad_panel/display.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import display
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
CONF_PAGES,
|
||||
CONF_CONTRAST,
|
||||
)
|
||||
|
||||
DEPENDENCIES = []
|
||||
|
||||
CONF_LATCH_PIN = 'latch_pin'
|
||||
CONF_CLOCK_PIN = 'clock_pin'
|
||||
CONF_DATA_PIN = 'data_pin'
|
||||
|
||||
obegraensadpanel_ns = cg.esphome_ns.namespace("obegraensadpanel")
|
||||
Panel = obegraensadpanel_ns.class_(
|
||||
"Panel", cg.PollingComponent, display.DisplayBuffer
|
||||
)
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Panel),
|
||||
cv.Required(CONF_LATCH_PIN): cv.int_,
|
||||
cv.Required(CONF_CLOCK_PIN): cv.int_,
|
||||
cv.Required(CONF_DATA_PIN): cv.int_,
|
||||
cv.Optional(CONF_CONTRAST, default=0x7F): cv.int_,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("200ms")),
|
||||
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await display.register_display(var, config)
|
||||
|
||||
cg.add(var.set_pins(
|
||||
config[CONF_LATCH_PIN],
|
||||
config[CONF_CLOCK_PIN],
|
||||
config[CONF_DATA_PIN],
|
||||
))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
166
components/obegraensad_panel/obegraensad-driver.cpp
Normal file
166
components/obegraensad_panel/obegraensad-driver.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copied From https://github.com/frumperino/FrekvensPanel
|
||||
// By /u/frumperino
|
||||
// goodwires.org
|
||||
|
||||
#include "obegraensad-driver.h"
|
||||
|
||||
// lookup tables for led-indices
|
||||
// stores the bottom-most value in each column, split for bottom and top half of the display
|
||||
const int lookupTop[] = {254, 14, 253, 13, 250, 10, 249, 9, 246, 6, 245, 5, 242, 2, 241, 1};
|
||||
const int lookupBottom[] = {255, 15, 252, 12, 251, 11, 248, 8, 247, 7, 244, 4, 243, 3, 240, 0};
|
||||
|
||||
ObegraensadPanel::ObegraensadPanel(int p_latch, int p_clock, int p_data) : Adafruit_GFX(16, 16)
|
||||
{
|
||||
init(p_latch, p_clock, p_data);
|
||||
}
|
||||
|
||||
void ObegraensadPanel::init(int p_latch, int p_clock, int p_data)
|
||||
{
|
||||
_pLatch = p_latch;
|
||||
_pClock = p_clock;
|
||||
_pData = p_data;
|
||||
pinMode(_pLatch, OUTPUT);
|
||||
pinMode(_pClock, OUTPUT);
|
||||
pinMode(_pData, OUTPUT);
|
||||
|
||||
_width = 16;
|
||||
_height = 16;
|
||||
|
||||
buf = (unsigned short*) malloc(16 * sizeof(unsigned short));
|
||||
}
|
||||
|
||||
// Converts Coordinates to LED Indices. Matrix has the schema:
|
||||
|
||||
// +---------+---------+---------+---------+---------+
|
||||
// | +----+ | +----+ | +----+ | +----+ | +----+ |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | v | | v | | v | | v | | v | |
|
||||
// | 254 14 |253 13 | <...> |242 2 |241 1 |
|
||||
// +---------+---------+---------+---------+---------+
|
||||
// | +----+ | +----+ | +----+ | +----+ | +----+ |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | | | | | | | | | | | | | | | |
|
||||
// | v | | v | | v | | v | | v | |
|
||||
// | 255 15 | 252 12 | <...> | 243 3 | 240 0 |
|
||||
// +---------+---------+---------+---------+---------+
|
||||
// along the arrows: increase by 16 per field
|
||||
unsigned short ObegraensadPanel::coordToIndex(int16_t x, int16_t y)
|
||||
{
|
||||
// Lookup Table for base column values
|
||||
auto lookup = (y > 7) ? lookupBottom : lookupTop;
|
||||
|
||||
// Offset from value in bottom-most row of this column
|
||||
auto columnOffset = (7 - (y % 8)) * 16;
|
||||
|
||||
int index;
|
||||
if (x % 2 == 0)
|
||||
{
|
||||
// even column --> led-indices in column go down (seen from bottom-most row)
|
||||
index = lookup[x] - columnOffset;
|
||||
} else
|
||||
{
|
||||
// odd column --> led-indices in column go up (seen from bottom-most row)
|
||||
index = lookup[x] + columnOffset;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void ObegraensadPanel::clear()
|
||||
{
|
||||
for (int i=0; i<16; i++)
|
||||
{
|
||||
buf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ObegraensadPanel::scan()
|
||||
{
|
||||
unsigned short* p = &buf[0];
|
||||
for (int i=0;i<16;i++)
|
||||
{
|
||||
unsigned short color = *p++;
|
||||
for (int j=0; j<16; j++)
|
||||
{
|
||||
digitalWrite(_pData, color & 0x01);
|
||||
color >>= 1;
|
||||
digitalWrite(_pClock, HIGH);
|
||||
delayMicroseconds(1);
|
||||
digitalWrite(_pClock, LOW);
|
||||
}
|
||||
}
|
||||
digitalWrite(_pLatch,HIGH);
|
||||
delayMicroseconds(1);
|
||||
digitalWrite(_pLatch,LOW);
|
||||
}
|
||||
|
||||
unsigned short ObegraensadPanel::width()
|
||||
{
|
||||
return this->_width;
|
||||
}
|
||||
|
||||
unsigned short ObegraensadPanel::height()
|
||||
{
|
||||
return this->_height;
|
||||
}
|
||||
|
||||
boolean ObegraensadPanel::getPixel(int16_t x, int16_t y)
|
||||
{
|
||||
//TODO: implement
|
||||
return false;
|
||||
}
|
||||
|
||||
void ObegraensadPanel::drawPixel(int16_t x, int16_t y, uint16_t color)
|
||||
{
|
||||
if ((x >= 0) && (y >= 0) && (x < _width) && ( y < _height))
|
||||
{
|
||||
// bit index from start of buf
|
||||
auto index = coordToIndex(x, y);
|
||||
|
||||
// transform index into page (unsigned short) + offset into the short
|
||||
auto pageIndex = index % 16;
|
||||
auto page = &buf[pageIndex];
|
||||
auto offset = index / 16;
|
||||
|
||||
// Shift Bit to correct position
|
||||
auto shifted = (1 << offset);
|
||||
|
||||
// If color is set add to page
|
||||
if (color != 0)
|
||||
{
|
||||
// OR to add to page
|
||||
*page |= shifted;
|
||||
} else
|
||||
{
|
||||
// AND with inverse to remove from page
|
||||
*page &= (0xFFFF ^ shifted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObegraensadPanel::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
|
||||
{
|
||||
for (int i=0;i<h;i++)
|
||||
{
|
||||
drawPixel(x,y+i,color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ObegraensadPanel::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
|
||||
{
|
||||
for (int i=0;i<w;i++)
|
||||
{
|
||||
drawPixel(x+i,y,color);
|
||||
}
|
||||
}
|
||||
|
||||
void ObegraensadPanel::fillScreen(uint16_t color) {
|
||||
auto fillColor = (color == 0) ? 0x00 : 0xFFFF;
|
||||
for (int i=0; i<16; i++)
|
||||
{
|
||||
buf[i] = fillColor;
|
||||
}
|
||||
}
|
||||
44
components/obegraensad_panel/obegraensad-driver.h
Normal file
44
components/obegraensad_panel/obegraensad-driver.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copied From https://github.com/frumperino/FrekvensPanel
|
||||
// by /u/frumperino
|
||||
// goodwires.org
|
||||
|
||||
#ifndef __OBEGRAENSADPANEL_H
|
||||
#define __OBEGRAENSADPANEL_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Adafruit_GFX.h>
|
||||
|
||||
class ObegraensadPanel : public Adafruit_GFX
|
||||
{
|
||||
private:
|
||||
unsigned short _pLatch;
|
||||
unsigned short _pClock;
|
||||
unsigned short _pData;
|
||||
unsigned short* buf;
|
||||
unsigned short _width;
|
||||
unsigned short _height;
|
||||
|
||||
public:
|
||||
ObegraensadPanel(int p_latch, int p_clock, int p_data);
|
||||
|
||||
void init(int p_latch, int p_clock, int p_data);
|
||||
void clear();
|
||||
void scan();
|
||||
|
||||
boolean getPixel(int16_t x, int16_t y);
|
||||
|
||||
unsigned short width();
|
||||
unsigned short height();
|
||||
|
||||
void drawPixel(int16_t x, int16_t y, uint16_t color) override;
|
||||
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override;
|
||||
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override;
|
||||
void fillScreen(uint16_t color) override;
|
||||
|
||||
private:
|
||||
unsigned short coordToIndex(int16_t x, int16_t y);
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //__OBEGRAENSADPANEL_H
|
||||
74
components/obegraensad_panel/obegraensad-panel.cpp
Normal file
74
components/obegraensad_panel/obegraensad-panel.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#include "obegraensad-panel.h"
|
||||
|
||||
|
||||
namespace esphome {
|
||||
namespace obegraensadpanel {
|
||||
|
||||
static const char *const TAG = "obegraensadpanel";
|
||||
|
||||
|
||||
void Panel::initialize() {
|
||||
this->init_internal_(this->get_buffer_length_());
|
||||
ESP_LOGV(TAG, "initializing Panel");
|
||||
this->panel = new ObegraensadPanel(this->p_latch, this->p_clock, this->p_data);
|
||||
}
|
||||
|
||||
int Panel::get_width_internal() { return 16; }
|
||||
int Panel::get_height_internal() { return 16; }
|
||||
|
||||
size_t Panel::get_buffer_length_() {
|
||||
return size_t(this->get_width_internal()) * size_t(this->get_height_internal());
|
||||
}
|
||||
|
||||
void HOT Panel::display() {
|
||||
uint8_t x, y, cell;
|
||||
|
||||
this->panel->clear();
|
||||
for (x = 0; x < this->get_width_internal(); x++) {
|
||||
for (y = 0; y < this->get_height_internal(); y++) {
|
||||
cell = this->buffer_[(this->get_width_internal() * y) + x];
|
||||
this->panel->drawPixel(x, y, cell);
|
||||
}
|
||||
}
|
||||
this->panel->scan();
|
||||
}
|
||||
|
||||
void HOT Panel::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || y >= this->get_height_internal() || x < 0 || y < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t pos = x + (y) * this->get_width_internal();
|
||||
uint8_t subpos = y % 8;
|
||||
|
||||
if (color.is_on()) {
|
||||
this->buffer_[pos] |= 1;
|
||||
} else {
|
||||
this->buffer_[pos] &= ~1;
|
||||
}
|
||||
}
|
||||
|
||||
void Panel::dump_config() {
|
||||
LOG_DISPLAY("", "Panel", this);
|
||||
LOG_PIN(" DC Pin: ", this->dc_pin_);
|
||||
LOG_PIN(" Reset Pin: ", this->reset_pin_);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void Panel::update() {
|
||||
this->do_update_();
|
||||
this->display();
|
||||
}
|
||||
|
||||
void Panel::fill(Color color) {
|
||||
uint8_t fill = color.is_on() ? 0xFF : 0x00;
|
||||
for (uint32_t i = 0; i < this->get_buffer_length_(); i++)
|
||||
this->buffer_[i] = fill;
|
||||
}
|
||||
|
||||
} // namespace obegraensadpanel
|
||||
} // namespace esphome
|
||||
65
components/obegraensad_panel/obegraensad-panel.h
Normal file
65
components/obegraensad_panel/obegraensad-panel.h
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
#include "obegraensad-driver.h"
|
||||
|
||||
|
||||
namespace esphome {
|
||||
namespace obegraensadpanel {
|
||||
|
||||
class Panel : public PollingComponent,
|
||||
public display::DisplayBuffer {
|
||||
public:
|
||||
int p_latch;
|
||||
int p_clock;
|
||||
int p_data;
|
||||
|
||||
void set_pins(int latch, int clock, int data) {
|
||||
this->p_latch = latch;
|
||||
this->p_clock = clock;
|
||||
this->p_data = data;
|
||||
}
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::PROCESSOR; }
|
||||
|
||||
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_BINARY; }
|
||||
|
||||
void data(uint8_t value);
|
||||
|
||||
void initialize();
|
||||
void dump_config() override;
|
||||
void HOT display();
|
||||
|
||||
void update() override;
|
||||
|
||||
void fill(Color color) override;
|
||||
|
||||
void setup() override {
|
||||
this->initialize();
|
||||
}
|
||||
|
||||
protected:
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
|
||||
void init_reset_();
|
||||
|
||||
size_t get_buffer_length_();
|
||||
|
||||
void start_command_();
|
||||
void end_command_();
|
||||
void start_data_();
|
||||
void end_data_();
|
||||
|
||||
int get_width_internal() override;
|
||||
int get_height_internal() override;
|
||||
|
||||
GPIOPin *reset_pin_;
|
||||
GPIOPin *dc_pin_;
|
||||
|
||||
ObegraensadPanel *panel;
|
||||
};
|
||||
|
||||
} // namespace pcd8544
|
||||
} // namespace esphome
|
||||
Reference in New Issue
Block a user