diff --git a/CMakeLists.txt b/CMakeLists.txt index fa2060e..92d652a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) +include($ENV{IOT_SOLUTION_PATH}/component.cmake) + project(blinky) target_add_binary_data(blinky.elf "main/broker.pem" TEXT) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 07abcfd..93d7397 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -11,6 +11,8 @@ idf_component_register( "leds.cpp" "presets.cpp" "spi_leds.cpp" + "screen_leds.cpp" + "display.cpp" "patterns/gradient.cpp" "patterns/random.cpp" "patterns/sparkle.cpp" diff --git a/main/display.cpp b/main/display.cpp new file mode 100644 index 0000000..5de5f03 --- /dev/null +++ b/main/display.cpp @@ -0,0 +1,80 @@ +static const char *TAG = "display"; +#include + + + +#include "display.hpp" + + +Display::Display(size_t w, size_t h) : + width(w), height(h), pitch(w), buffer(NULL), + bus(NULL), iface_drv(NULL), driver{.deinit = NULL} + { + esp_err_t ret; + + spi_config_t bus_cfg = { + .miso_io_num = GPIO_NUM_4, + .mosi_io_num = GPIO_NUM_35, + .sclk_io_num = GPIO_NUM_36, + .max_transfer_sz = (int)(height * width * sizeof(*buffer) + 8), + }; + + bus = spi_bus_create(SPI2_HOST, &bus_cfg); + if (NULL == bus) { + ESP_LOGE(TAG, "Failed to acquire bus"); + return; + } + + scr_interface_spi_config_t spi_dev_cfg = { + .spi_bus = bus, + .pin_num_cs = 34, + .pin_num_dc = 37, + .clk_freq = 40 * 1000 * 1000, + .swap_data = 1, + }; + + ret = scr_interface_create(SCREEN_IFACE_SPI, &spi_dev_cfg, &iface_drv); + if (ESP_OK != ret) { + ESP_LOGE(TAG, "Failed to create interface"); + return; + } + + ret = scr_find_driver(SCREEN_CONTROLLER_ST7789, &driver); + if (ESP_OK != ret) { + ESP_LOGE(TAG, "Failed to find driver"); + return; + } + + scr_controller_config_t lcd_cfg = { + .interface_drv = iface_drv, + .pin_num_rst = 38, + .pin_num_bckl = 33, + .rst_active_level = 0, + .bckl_active_level = 1, + .width = (uint16_t)width, + .height = (uint16_t)height, + .offset_hor = 52, + .offset_ver = 40, + .rotate = SCR_DIR_LRTB, + }; + + ret = driver.init(&lcd_cfg); + if (ESP_OK != ret) { + ESP_LOGE(TAG, "Failed to initialize driver"); + return; + } + + buffer = new uint16_t[height * pitch](); + refresh(); +} + +Display::~Display() { + if (buffer) delete[] buffer; + if (driver.deinit) driver.deinit(); + if (iface_drv) scr_interface_delete(iface_drv); + if (bus) spi_bus_delete(&bus); +} + +void Display::refresh() { + driver.draw_bitmap(0, 0, width, height, buffer); +} diff --git a/main/display.hpp b/main/display.hpp new file mode 100644 index 0000000..b458999 --- /dev/null +++ b/main/display.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + + +class Display { +public: + Display(size_t w, size_t h); + ~Display(); + + void refresh(); + + size_t get_width() const { return width; } + size_t get_height() const { return height; } + size_t get_pitch() const { return pitch; } + size_t get_stride() const { return pitch * sizeof(*buffer); } + uint16_t* get_buffer() const { return buffer; } + +private: + size_t width; + size_t height; + size_t pitch; + uint16_t *buffer; + + spi_bus_handle_t bus; + scr_interface_driver_t *iface_drv; + scr_driver_t driver; +}; diff --git a/main/main.cpp b/main/main.cpp index 5cc1811..3e3b243 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -16,6 +16,7 @@ static const char *TAG = "blinky"; #include "presets.hpp" #include "utils.hpp" #include "spi_leds.hpp" +#include "screen_leds.hpp" // mDNS / NetBIOS @@ -176,7 +177,7 @@ extern "C" void app_main(void) { } else { ESP_LOGI(TAG, "Starting animation"); - int period_us = 1000000 / 60; + int period_us = 1000000 / 30; int64_t target_us = time_us(); int64_t profile_start = target_us; #ifdef PROFILE_PERF diff --git a/main/screen_leds.cpp b/main/screen_leds.cpp new file mode 100644 index 0000000..946e079 --- /dev/null +++ b/main/screen_leds.cpp @@ -0,0 +1,79 @@ +static const char *TAG = "screen_leds"; +#include + +#include "screen_leds.hpp" + + +static inline void fill(uint16_t *buffer, uint16_t value, int w, int h, int p = -1) { + if (p <= 0) p = w; + uint16_t *line = buffer; + while (--h >= 0) { + uint16_t *pixel = line; + for (int _w = w; _w > 0; --_w) { + *pixel = value; + ++pixel; + } + line += p; + } +} + +static inline void box(uint16_t *buffer, uint16_t value, int x, int y, int w, int h, int p = -1) { + if (p <= 0) p = w; + fill(buffer + x + (y * p), value, w, h, p); +} + +static inline uint16_t rgb16(int r, int g, int b) { + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | ((uint8_t)b >> 3); +} + +static inline uint16_t color_to_565(Color c) { + return rgb16(c.r, c.g, c.b); +} + +ScreenLEDs::ScreenLEDs(int length) : LEDStrip(length), display(135, 240) {} + +void ScreenLEDs::length_changing(int length) { + fill(display.get_buffer(), COLOR_BLACK, display.get_width(), display.get_height()); +} + +void ScreenLEDs::show() { + const int width = display.get_width(); + const int height = display.get_height(); + const int pitch = display.get_pitch(); + auto buffer = display.get_buffer(); + + const size_t size = ((width + height) * 2) / (length + 4); + + int lx = 0, ly = 0; + int dx = 0, dy = size; + for (size_t l = 0; l < length; ++l) { + if (ly + dy > height) { + ly -= dy; + dx = size; + dy = 0; + lx += dx; + } else if (ly < 0) { + ly -= dy; + dx = -size; + dy = 0; + lx += dx; + } else if (lx + dx > width) { + lx -= dx; + dx = 0; + dy = -size; + ly += dy; + } else if (lx < 0) { + lx -= dx; + dx = 0; + dy = size; + ly += dy; + } + + box(buffer, color_to_565(pixels[l]), lx, ly, size, size, pitch); + + lx += dx; + ly += dy; + } + + display.refresh(); +} diff --git a/main/screen_leds.hpp b/main/screen_leds.hpp new file mode 100644 index 0000000..7277968 --- /dev/null +++ b/main/screen_leds.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "leds.hpp" +#include "display.hpp" + + +class ScreenLEDs : public LEDStrip { +public: + ScreenLEDs(int length = 0); + void show(); + void length_changing(int length); + +private: + Display display; +};