Skip to content

Commit

Permalink
first tentative
Browse files Browse the repository at this point in the history
  • Loading branch information
jcelerier committed Aug 12, 2024
1 parent 7b8b210 commit ec94021
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 22 deletions.
1 change: 0 additions & 1 deletion .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ BreakBeforeBraces: Allman
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 79
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
Expand Down
164 changes: 160 additions & 4 deletions Led/Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,174 @@

#include <score/document/DocumentContext.hpp>

#include <ossia/detail/timer.hpp>
#include <ossia/network/base/device.hpp>
#include <ossia/network/base/node.hpp>
#include <ossia/network/base/parameter.hpp>
#include <ossia/network/base/protocol.hpp>
#include <ossia/network/common/complex_type.hpp>
#include <ossia/network/context.hpp>
#include <ossia/network/generic/generic_device.hpp>
#include <ossia/network/local/local.hpp>

#include <boost/container/static_vector.hpp>

#include <QDebug>

#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <wobjectimpl.h>

W_OBJECT_IMPL(Led::DeviceImplementation)

namespace Led
{
struct led_protocol : public ossia::net::protocol_base
{
led_protocol(
const SpecificSettings& set,
ossia::net::network_context_ptr ctx)
: protocol_base{flags{}}
, m_context{std::move(ctx)}
, m_timer{m_context->context}
, m_pixels{set.num_pixels}
, m_speed{set.speed}
{

using namespace std::literals;
m_fd = ::open(set.device.toStdString().c_str(), O_RDWR);
if (m_fd != 0)
throw std::runtime_error("Could not open "s + set.device.toStdString());

constexpr float frequency = 10;
m_timer.set_delay(std::chrono::milliseconds{
static_cast<int>(1000.0f / static_cast<float>(frequency))});
}

~led_protocol()
{
::close(m_fd);
m_timer.stop();
}

void set_device(ossia::net::device_base& dev) override
{
m_device = &dev;

auto& root = m_device->get_root_node();

strip = ossia::create_parameter(root, "/strip", "list");
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;
}
return color;
}

static auto RGB2BitBang(uint8_t r, uint8_t g, uint8_t b) noexcept
{
using namespace std;
const auto r_array = int_to_neopixel(r);
const auto g_array = int_to_neopixel(g);
const auto b_array = int_to_neopixel(b);

std::array<uint8_t, 24> fullColor;
#pragma omp simd
for (int cnt = 0; cnt < 8; ++cnt)
{
fullColor[cnt + 0] = r_array[cnt];
fullColor[cnt + 8] = g_array[cnt];
fullColor[cnt + 16] = b_array[cnt];
}
return fullColor;
}

bool pull(ossia::net::parameter_base& v) override { return false; }

bool
push(const ossia::net::parameter_base& p, const ossia::value& v) override
{
// FIXME: update a single pixel efficiently
auto val = v.target<std::vector<ossia::value>>();
if (!val)
return false;

m_bitbang_data.clear();
m_bitbang_data.reserve(m_pixels * 24);
int N = std::min(m_pixels, (int)std::ssize(*val));

for (int i = 0; i < N; i++)
{
auto col = ossia::convert<ossia::vec3f>((*val)[i]);

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;

// 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());
}

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);
return true;
}

bool push_raw(const ossia::net::full_parameter_data&) override
{
return false;
}

bool observe(ossia::net::parameter_base&, bool) override { return false; }

bool update(ossia::net::node_base& node_base) override { return false; }

void update_function() { }

ossia::net::network_context_ptr m_context;
ossia::net::device_base* m_device{};
ossia::timer m_timer;

int m_pixels{};
int m_speed{};

struct rgb
{
uint8_t r, g, b;
};
std::vector<uint8_t> m_bitbang_data;
ossia::net::parameter_base* strip{};
int m_fd{-1};
};

DeviceImplementation::DeviceImplementation(
const Device::DeviceSettings& settings,
const Explorer::DeviceDocumentPlugin& plugin,
Expand All @@ -42,12 +199,11 @@ bool DeviceImplementation::reconnect()
{
const auto& set
= m_settings.deviceSpecificSettings.value<SpecificSettings>();
qDebug() << "Led created with: " << set.control;

// Needed by most protocols:
auto& ctx = m_ctx.networkContext();

auto protocol = std::make_unique<ossia::net::multiplex_protocol>();
auto protocol = std::make_unique<led_protocol>(set, ctx);
auto dev = std::make_unique<ossia::net::generic_device>(
std::move(protocol), settings().name.toStdString());

Expand All @@ -70,4 +226,4 @@ void DeviceImplementation::disconnect()
{
OwningDeviceInterface::disconnect();
}
}
}
7 changes: 5 additions & 2 deletions Led/ProtocolFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace Led

QString ProtocolFactory::prettyName() const noexcept
{
return QObject::tr("Led");
return QObject::tr("NeoPixel LEDs");
}

QString ProtocolFactory::category() const noexcept
Expand Down Expand Up @@ -50,6 +50,9 @@ const Device::DeviceSettings& ProtocolFactory::defaultSettings() const noexcept
s.protocol = concreteKey();
s.name = "Led";
SpecificSettings settings;
settings.device = "/dev/spidev0.0";
settings.num_pixels = 12;
settings.speed = 100;
s.deviceSpecificSettings = QVariant::fromValue(settings);
return s;
}();
Expand Down Expand Up @@ -79,6 +82,6 @@ bool ProtocolFactory::checkCompatibility(
const Device::DeviceSettings& a,
const Device::DeviceSettings& b) const noexcept
{
return false;
return true;
}
}
19 changes: 12 additions & 7 deletions Led/ProtocolSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,16 @@ ProtocolSettingsWidget::ProtocolSettingsWidget(QWidget* parent)
m_deviceNameEdit = new State::AddressFragmentLineEdit{this};
m_deviceNameEdit->setText("Led");

m_control = new QSpinBox{this};
m_control->setRange(1, 65535);
m_spiDevice = new QLineEdit{this};
m_spiDevice->setText("/dev/spidev0.0");

m_pixels = new QSpinBox{this};
m_pixels->setRange(1, 65535);

auto layout = new QFormLayout;
layout->addRow(tr("Name"), m_deviceNameEdit);
layout->addRow(tr("Control"), m_control);
layout->addRow(tr("Device"), m_spiDevice);
layout->addRow(tr("Pixels"), m_pixels);

setLayout(layout);
}
Expand All @@ -52,13 +56,13 @@ ProtocolSettingsWidget::~ProtocolSettingsWidget() { }

Device::DeviceSettings ProtocolSettingsWidget::getSettings() const
{
// TODO should be = m_settings to follow the other patterns.
Device::DeviceSettings s;
s.name = m_deviceNameEdit->text();
s.protocol = ProtocolFactory::static_concreteKey();

SpecificSettings settings{};
settings.control = this->m_control->value();
settings.device = this->m_spiDevice->text();
settings.num_pixels = this->m_pixels->value();
s.deviceSpecificSettings = QVariant::fromValue(settings);

return s;
Expand All @@ -70,6 +74,7 @@ void ProtocolSettingsWidget::setSettings(
m_deviceNameEdit->setText(settings.name);
const auto& specif
= settings.deviceSpecificSettings.value<SpecificSettings>();
m_control->setValue(specif.control);
m_spiDevice->setText(specif.device);
m_pixels->setValue(specif.num_pixels);
}
}
}
3 changes: 2 additions & 1 deletion Led/ProtocolSettingsWidget.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class ProtocolSettingsWidget final : public Device::ProtocolSettingsWidget

private:
QLineEdit* m_deviceNameEdit{};
QSpinBox* m_control{};
QLineEdit* m_spiDevice{};
QSpinBox* m_pixels{};
};
}
6 changes: 4 additions & 2 deletions Led/SpecificSettings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ namespace Led
{
struct SpecificSettings
{
int control{1234};
QString device;
int num_pixels{12};
int speed{100};
};
}

Q_DECLARE_METATYPE(Led::SpecificSettings)
W_REGISTER_ARGTYPE(Led::SpecificSettings)
W_REGISTER_ARGTYPE(Led::SpecificSettings)
14 changes: 9 additions & 5 deletions Led/SpecificSettingsSerialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,29 @@
template <>
void DataStreamReader::read(const Led::SpecificSettings& n)
{
m_stream << n.control;
m_stream << n.device << n.num_pixels << n.speed;
insertDelimiter();
}

template <>
void DataStreamWriter::write(Led::SpecificSettings& n)
{
m_stream >> n.control;
m_stream >> n.device >> n.num_pixels >> n.speed;
checkDelimiter();
}

template <>
void JSONReader::read(const Led::SpecificSettings& n)
{
obj["Control"] = n.control;
obj["Device"] = n.device;
obj["Pixels"] = n.num_pixels;
obj["Speed"] = n.speed;
}

template <>
void JSONWriter::write(Led::SpecificSettings& n)
{
n.control <<= obj["Control"];
}
n.device <<= obj["Device"];
n.num_pixels <<= obj["Pixels"];
n.speed <<= obj["Speed"];
}
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# Led
A new and wonderful [ossia score](https://ossia.io) add-on

Note: https://github.com/jgarff/rpi_ws281x/issues/447

Minimum:

```
echo 700000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
echo 700000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
```

0 comments on commit ec94021

Please sign in to comment.