diff --git a/Led/Device.cpp b/Led/Device.cpp index 87a3675..83001e5 100644 --- a/Led/Device.cpp +++ b/Led/Device.cpp @@ -29,6 +29,30 @@ W_OBJECT_IMPL(Led::DeviceImplementation) namespace Led { + +// Code adapted from https://github.com/hannescam/NeoSPI +static constexpr std::array int_to_neopixel(uint8_t val) noexcept +{ + std::array color; + color.fill(0xC0); + + for (int cnt = 0; cnt < 8; cnt++) + { + if (val & (1 << (7 - cnt))) + color[cnt] = 0xF8; + } + return color; +} + +static constexpr std::array, 255> int_to_neopixel_table + = []() constexpr +{ + std::array, 255> res; + for (uint8_t i = 0; i < 255; i++) + res[i] = int_to_neopixel(i); + return res; +}(); + struct led_protocol : public ossia::net::protocol_base { led_protocol( @@ -39,6 +63,7 @@ struct led_protocol : public ossia::net::protocol_base , m_timer{m_context->context} , m_pixels{set.num_pixels} , m_speed{set.speed} + , m_format{set.format} { using namespace std::literals; @@ -73,21 +98,29 @@ struct led_protocol : public ossia::net::protocol_base strip->get_node(), std::to_string(i), "rgb")); } if (m_fd >= 0) - m_timer.start([this] { update_function(); }); - } - - // Code adapted from https://github.com/hannescam/NeoSPI - static constexpr std::array int_to_neopixel(uint8_t val) noexcept - { - std::array color; - color.fill(0xC0); - - for (int cnt = 0; cnt < 8; cnt++) { - if (val & (1 << (7 - cnt))) - color[cnt] = 0xF8; + switch (m_format) + { + case NeoPixelsFormat::GRB: + m_timer.start( + [this] + { + init_bitbang(); + update_function_grb(); + push_to_spi(); + }); + break; + case NeoPixelsFormat::RGB: + m_timer.start( + [this] + { + init_bitbang(); + update_function_rgb(); + push_to_spi(); + }); + break; + } } - return color; } static auto rgb_to_bitbang(uint8_t r, uint8_t g, uint8_t b) noexcept @@ -113,17 +146,7 @@ struct led_protocol : public ossia::net::protocol_base bool push(const ossia::net::parameter_base& p, const ossia::value& v) override { - if (&p == this->strip) - { - auto val = v.target>(); - if (!val) - return false; - return true; - } - else - { - } - return true; + return false; } bool push_raw(const ossia::net::full_parameter_data&) override @@ -135,43 +158,72 @@ struct led_protocol : public ossia::net::protocol_base bool update(ossia::net::node_base& node_base) override { return false; } - void update_function() + void init_bitbang() { m_bitbang_data.clear(); m_bitbang_data.reserve(m_pixels * 24); - const int N = std::min(m_pixels, (int)std::ssize(pixels)); + } + void push_to_spi() + { + struct spi_ioc_transfer spi; + memset(&spi, 0, sizeof(spi)); + spi.tx_buf = reinterpret_cast(m_bitbang_data.data()); + spi.len = m_pixels * 24; + spi.delay_usecs = 0; + spi.speed_hz = m_speed * 8 * 1024; + spi.bits_per_word = 8; + ioctl(m_fd, SPI_IOC_MESSAGE(1), &spi); + } + + void update_function_rgb() + { + const int N = std::min(m_pixels, (int)std::ssize(pixels)); +#pragma omp simd for (int i = 0; i < N; i++) { auto col = ossia::convert(pixels[i]->value()); - auto rgb_col = ossia::rgb{col}; - // 1. ossia::vec3f to basic rgb - const int r = std::clamp(rgb_col.dataspace_value[0], 0.f, 1.f) * 255; - const int g = std::clamp(rgb_col.dataspace_value[1], 0.f, 1.f) * 255; - const int b = std::clamp(rgb_col.dataspace_value[2], 0.f, 1.f) * 255; + const uint8_t r = std::clamp(col[0], 0.f, 1.f) * 255; + const uint8_t g = std::clamp(col[1], 0.f, 1.f) * 255; + const uint8_t b = std::clamp(col[2], 0.f, 1.f) * 255; // 2. rgb to bitbangable rgb - const auto r_array = int_to_neopixel(r); - const auto g_array = int_to_neopixel(g); - const auto b_array = int_to_neopixel(b); - m_bitbang_data.insert( - m_bitbang_data.end(), r_array.begin(), r_array.end()); - m_bitbang_data.insert( - m_bitbang_data.end(), g_array.begin(), g_array.end()); - m_bitbang_data.insert( - m_bitbang_data.end(), b_array.begin(), b_array.end()); + const auto& r_array = int_to_neopixel_table[r]; + const auto& g_array = int_to_neopixel_table[g]; + const auto& b_array = int_to_neopixel_table[b]; + + uint8_t* data = m_bitbang_data.data() + i * 24; + std::copy_n(r_array.data(), 8, data); + std::copy_n(g_array.data(), 8, data + 8); + std::copy_n(b_array.data(), 8, data + 16); } + } - struct spi_ioc_transfer spi; - memset(&spi, 0, sizeof(spi)); - spi.tx_buf = reinterpret_cast(m_bitbang_data.data()); - spi.len = m_pixels * 24; - spi.delay_usecs = 0; - spi.speed_hz = m_speed * 8 * 1024; - spi.bits_per_word = 8; - ioctl(m_fd, SPI_IOC_MESSAGE(1), &spi); + void update_function_grb() + { + const int N = std::min(m_pixels, (int)std::ssize(pixels)); +#pragma omp simd + for (int i = 0; i < N; i++) + { + auto col = ossia::convert(pixels[i]->value()); + + // 1. ossia::vec3f to basic rgb + const uint8_t g = std::clamp(col[1], 0.f, 1.f) * 255; + const uint8_t r = std::clamp(col[0], 0.f, 1.f) * 255; + const uint8_t b = std::clamp(col[2], 0.f, 1.f) * 255; + + // 2. rgb to bitbangable grb + const auto& g_array = int_to_neopixel_table[g]; + const auto& r_array = int_to_neopixel_table[r]; + const auto& b_array = int_to_neopixel_table[b]; + + uint8_t* data = m_bitbang_data.data() + i * 24; + std::copy_n(g_array.data(), 8, data); + std::copy_n(r_array.data(), 8, data + 8); + std::copy_n(b_array.data(), 8, data + 16); + } } ossia::net::network_context_ptr m_context; @@ -180,6 +232,7 @@ struct led_protocol : public ossia::net::protocol_base int m_pixels{}; int m_speed{}; + NeoPixelsFormat m_format{}; std::vector m_bitbang_data; ossia::net::parameter_base* strip{}; diff --git a/Led/ProtocolSettingsWidget.cpp b/Led/ProtocolSettingsWidget.cpp index 1997fea..1aab386 100644 --- a/Led/ProtocolSettingsWidget.cpp +++ b/Led/ProtocolSettingsWidget.cpp @@ -44,10 +44,14 @@ ProtocolSettingsWidget::ProtocolSettingsWidget(QWidget* parent) m_pixels = new QSpinBox{this}; m_pixels->setRange(1, 65535); + m_format = new QComboBox{this}; + m_format->addItems({"GRB", "RGB"}); + auto layout = new QFormLayout; layout->addRow(tr("Name"), m_deviceNameEdit); layout->addRow(tr("Device"), m_spiDevice); layout->addRow(tr("Pixels"), m_pixels); + layout->addRow(tr("Format"), m_format); setLayout(layout); } @@ -63,6 +67,8 @@ Device::DeviceSettings ProtocolSettingsWidget::getSettings() const SpecificSettings settings{}; settings.device = this->m_spiDevice->text(); settings.num_pixels = this->m_pixels->value(); + settings.format + = static_cast(this->m_format->currentIndex()); s.deviceSpecificSettings = QVariant::fromValue(settings); return s; @@ -76,5 +82,6 @@ void ProtocolSettingsWidget::setSettings( = settings.deviceSpecificSettings.value(); m_spiDevice->setText(specif.device); m_pixels->setValue(specif.num_pixels); + m_format->setCurrentIndex(static_cast(specif.format)); } } diff --git a/Led/ProtocolSettingsWidget.hpp b/Led/ProtocolSettingsWidget.hpp index 632cc4a..806234d 100644 --- a/Led/ProtocolSettingsWidget.hpp +++ b/Led/ProtocolSettingsWidget.hpp @@ -4,7 +4,6 @@ #include -#include #include class QLineEdit; @@ -19,7 +18,7 @@ class ProtocolSettingsWidget final : public Device::ProtocolSettingsWidget W_OBJECT(ProtocolSettingsWidget) public: - ProtocolSettingsWidget(QWidget* parent = nullptr); + explicit ProtocolSettingsWidget(QWidget* parent = nullptr); virtual ~ProtocolSettingsWidget(); Device::DeviceSettings getSettings() const override; void setSettings(const Device::DeviceSettings& settings) override; @@ -28,5 +27,6 @@ class ProtocolSettingsWidget final : public Device::ProtocolSettingsWidget QLineEdit* m_deviceNameEdit{}; QLineEdit* m_spiDevice{}; QSpinBox* m_pixels{}; + QComboBox* m_format{}; }; } diff --git a/Led/SpecificSettings.hpp b/Led/SpecificSettings.hpp index 4138086..475edf1 100644 --- a/Led/SpecificSettings.hpp +++ b/Led/SpecificSettings.hpp @@ -5,11 +5,18 @@ namespace Led { +enum class NeoPixelsFormat +{ + GRB, + RGB +}; + struct SpecificSettings { QString device; int num_pixels{12}; - int speed{100}; + int speed{800}; + NeoPixelsFormat format{NeoPixelsFormat::GRB}; }; } diff --git a/Led/SpecificSettingsSerialization.cpp b/Led/SpecificSettingsSerialization.cpp index 1556f19..ffd199d 100644 --- a/Led/SpecificSettingsSerialization.cpp +++ b/Led/SpecificSettingsSerialization.cpp @@ -6,14 +6,14 @@ template <> void DataStreamReader::read(const Led::SpecificSettings& n) { - m_stream << n.device << n.num_pixels << n.speed; + m_stream << n.device << n.num_pixels << n.speed << n.format; insertDelimiter(); } template <> void DataStreamWriter::write(Led::SpecificSettings& n) { - m_stream >> n.device >> n.num_pixels >> n.speed; + m_stream >> n.device >> n.num_pixels >> n.speed >> n.format; checkDelimiter(); } @@ -23,6 +23,7 @@ void JSONReader::read(const Led::SpecificSettings& n) obj["Device"] = n.device; obj["Pixels"] = n.num_pixels; obj["Speed"] = n.speed; + obj["Format"] = n.format; } template <> @@ -31,4 +32,5 @@ void JSONWriter::write(Led::SpecificSettings& n) n.device <<= obj["Device"]; n.num_pixels <<= obj["Pixels"]; n.speed <<= obj["Speed"]; + n.format <<= obj["Format"]; }