Skip to content

Commit

Permalink
Allow to choose between GRB and RGB format, fix default speed
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Aug 12, 2024
1 parent 7c1ef99 commit 9b46ac1
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 53 deletions.
149 changes: 101 additions & 48 deletions Led/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,30 @@ W_OBJECT_IMPL(Led::DeviceImplementation)

namespace Led
{

// Code adapted from https://github.com/hannescam/NeoSPI
static constexpr std::array<uint8_t, 8> int_to_neopixel(uint8_t val) noexcept
{
std::array<uint8_t, 8> 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<std::array<uint8_t, 8>, 255> int_to_neopixel_table
= []() constexpr
{
std::array<std::array<uint8_t, 8>, 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(
Expand All @@ -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;
Expand Down Expand Up @@ -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<uint8_t, 8> int_to_neopixel(uint8_t val) noexcept
{
std::array<uint8_t, 8> 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
Expand All @@ -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<std::vector<ossia::value>>();
if (!val)
return false;
return true;
}
else
{
}
return true;
return false;
}

bool push_raw(const ossia::net::full_parameter_data&) override
Expand All @@ -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<std::uintptr_t>(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<ossia::vec3f>(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<std::uintptr_t>(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<ossia::vec3f>(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;
Expand All @@ -180,6 +232,7 @@ struct led_protocol : public ossia::net::protocol_base

int m_pixels{};
int m_speed{};
NeoPixelsFormat m_format{};

std::vector<uint8_t> m_bitbang_data;
ossia::net::parameter_base* strip{};
Expand Down
7 changes: 7 additions & 0 deletions Led/ProtocolSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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<NeoPixelsFormat>(this->m_format->currentIndex());
s.deviceSpecificSettings = QVariant::fromValue(settings);

return s;
Expand All @@ -76,5 +82,6 @@ void ProtocolSettingsWidget::setSettings(
= settings.deviceSpecificSettings.value<SpecificSettings>();
m_spiDevice->setText(specif.device);
m_pixels->setValue(specif.num_pixels);
m_format->setCurrentIndex(static_cast<int>(specif.format));
}
}
4 changes: 2 additions & 2 deletions Led/ProtocolSettingsWidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include <Led/SpecificSettings.hpp>

#include <variant>
#include <verdigris>

class QLineEdit;
Expand All @@ -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;
Expand All @@ -28,5 +27,6 @@ class ProtocolSettingsWidget final : public Device::ProtocolSettingsWidget
QLineEdit* m_deviceNameEdit{};
QLineEdit* m_spiDevice{};
QSpinBox* m_pixels{};
QComboBox* m_format{};
};
}
9 changes: 8 additions & 1 deletion Led/SpecificSettings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
};
}

Expand Down
6 changes: 4 additions & 2 deletions Led/SpecificSettingsSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand All @@ -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 <>
Expand All @@ -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"];
}

0 comments on commit 9b46ac1

Please sign in to comment.