Add hyperion-usbasp led devices

Remove all WS281x direct UART code (does not work reliable)


Former-commit-id: cd8103058d4ce0cd3280c7a2c5370397a14acf5c
This commit is contained in:
johan
2014-03-09 11:36:46 +01:00
parent dd0a18642b
commit e22c720e68
14 changed files with 308 additions and 1638 deletions

View File

@@ -28,8 +28,7 @@ SET(Leddevice_HEADERS
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h
${CURRENT_SOURCE_DIR}/LedDeviceSedu.h
${CURRENT_SOURCE_DIR}/LedDeviceTest.h
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.h
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.h
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h
)
SET(Leddevice_SOURCES
@@ -44,8 +43,7 @@ SET(Leddevice_SOURCES
${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp
${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp
${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp
${CURRENT_SOURCE_DIR}/LedDeviceWs2811.cpp
${CURRENT_SOURCE_DIR}/LedDeviceWs2812b.cpp
${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp
)
if(ENABLE_SPIDEV)

View File

@@ -23,8 +23,7 @@
#include "LedDevicePiBlaster.h"
#include "LedDeviceSedu.h"
#include "LedDeviceTest.h"
#include "LedDeviceWs2811.h"
#include "LedDeviceWs2812b.h"
#include "LedDeviceHyperionUsbasp.h"
LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
{
@@ -87,23 +86,6 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
device = deviceWs2801;
}
#endif
// else if (type == "ws2811")
// {
// const std::string output = deviceConfig["output"].asString();
// const std::string outputSpeed = deviceConfig["output"].asString();
// const std::string timingOption = deviceConfig["timingOption"].asString();
// ws2811::SpeedMode speedMode = (outputSpeed == "high")? ws2811::highspeed : ws2811::lowspeed;
// if (outputSpeed != "high" && outputSpeed != "low")
// {
// std::cerr << "Incorrect speed-mode selected for WS2811: " << outputSpeed << " != {'high', 'low'}" << std::endl;
// }
// LedDeviceWs2811 * deviceWs2811 = new LedDeviceWs2811(output, ws2811::fromString(timingOption, ws2811::option_2855), speedMode);
// deviceWs2811->open();
// device = deviceWs2811;
// }
else if (type == "lightpack")
{
const std::string output = deviceConfig.get("output", "").asString();
@@ -147,18 +129,23 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig)
device = deviceSedu;
}
else if (type == "hyperion-usbasp-ws2801")
{
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2801);
deviceHyperionUsbasp->open();
device = deviceHyperionUsbasp;
}
else if (type == "hyperion-usbasp-ws2812")
{
LedDeviceHyperionUsbasp * deviceHyperionUsbasp = new LedDeviceHyperionUsbasp(LedDeviceHyperionUsbasp::CMD_WRITE_WS2812);
deviceHyperionUsbasp->open();
device = deviceHyperionUsbasp;
}
else if (type == "test")
{
const std::string output = deviceConfig["output"].asString();
device = new LedDeviceTest(output);
}
else if (type == "ws2812b")
{
LedDeviceWs2812b * deviceWs2812b = new LedDeviceWs2812b();
deviceWs2812b->open();
device = deviceWs2812b;
}
else
{
std::cout << "Unable to create device " << type << std::endl;

View File

@@ -0,0 +1,205 @@
// stl includes
#include <exception>
#include <cstring>
// Local Hyperion includes
#include "LedDeviceHyperionUsbasp.h"
// Static constants which define the Hyperion Usbasp device
uint16_t LedDeviceHyperionUsbasp::_usbVendorId = 0x16c0;
uint16_t LedDeviceHyperionUsbasp::_usbProductId = 0x05dc;
std::string LedDeviceHyperionUsbasp::_usbProductDescription = "Hyperion led controller";
LedDeviceHyperionUsbasp::LedDeviceHyperionUsbasp(uint8_t writeLedsCommand) :
LedDevice(),
_writeLedsCommand(writeLedsCommand),
_libusbContext(nullptr),
_deviceHandle(nullptr),
_ledCount(256)
{
}
LedDeviceHyperionUsbasp::~LedDeviceHyperionUsbasp()
{
if (_deviceHandle != nullptr)
{
libusb_release_interface(_deviceHandle, 0);
libusb_attach_kernel_driver(_deviceHandle, 0);
libusb_close(_deviceHandle);
_deviceHandle = nullptr;
}
if (_libusbContext != nullptr)
{
libusb_exit(_libusbContext);
_libusbContext = nullptr;
}
}
int LedDeviceHyperionUsbasp::open()
{
int error;
// initialize the usb context
if ((error = libusb_init(&_libusbContext)) != LIBUSB_SUCCESS)
{
std::cerr << "Error while initializing USB context(" << error << "): " << libusb_error_name(error) << std::endl;
_libusbContext = nullptr;
return -1;
}
//libusb_set_debug(_libusbContext, 3);
std::cout << "USB context initialized" << std::endl;
// retrieve the list of usb devices
libusb_device ** deviceList;
ssize_t deviceCount = libusb_get_device_list(_libusbContext, &deviceList);
// iterate the list of devices
for (ssize_t i = 0 ; i < deviceCount; ++i)
{
// try to open and initialize the device
error = testAndOpen(deviceList[i]);
if (error == 0)
{
// a device was sucessfully opened. break from list
break;
}
}
// free the device list
libusb_free_device_list(deviceList, 1);
if (_deviceHandle == nullptr)
{
std::cerr << "No " << _usbProductDescription << " has been found" << std::endl;
}
return _deviceHandle == nullptr ? -1 : 0;
}
int LedDeviceHyperionUsbasp::testAndOpen(libusb_device * device)
{
libusb_device_descriptor deviceDescriptor;
int error = libusb_get_device_descriptor(device, &deviceDescriptor);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "Error while retrieving device descriptor(" << error << "): " << libusb_error_name(error) << std::endl;
return -1;
}
if (deviceDescriptor.idVendor == _usbVendorId &&
deviceDescriptor.idProduct == _usbProductId &&
deviceDescriptor.iProduct != 0 &&
getString(device, deviceDescriptor.iProduct) == _usbProductDescription)
{
// get the hardware address
int busNumber = libusb_get_bus_number(device);
int addressNumber = libusb_get_device_address(device);
std::cout << _usbProductDescription << " found: bus=" << busNumber << " address=" << addressNumber << std::endl;
try
{
_deviceHandle = openDevice(device);
std::cout << _usbProductDescription << " successfully opened" << std::endl;
return 0;
}
catch(int e)
{
_deviceHandle = nullptr;
std::cerr << "Unable to open " << _usbProductDescription << ". Searching for other device(" << e << "): " << libusb_error_name(e) << std::endl;
}
}
return -1;
}
int LedDeviceHyperionUsbasp::write(const std::vector<ColorRgb> &ledValues)
{
_ledCount = ledValues.size();
int nbytes = libusb_control_transfer(
_deviceHandle, // device handle
LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE | LIBUSB_ENDPOINT_OUT, // request type
_writeLedsCommand, // request
0, // value
0, // index
(uint8_t *) ledValues.data(), // data
(3*_ledCount) & 0xffff, // length
5000); // timeout
// Disabling interupts for a little while on the device results in a PIPE error. All seems to keep functioning though...
if(nbytes < 0 && nbytes != LIBUSB_ERROR_PIPE)
{
std::cerr << "Error while writing data to " << _usbProductDescription << " (" << libusb_error_name(nbytes) << ")" << std::endl;
return -1;
}
return 0;
}
int LedDeviceHyperionUsbasp::switchOff()
{
std::vector<ColorRgb> ledValues(_ledCount, ColorRgb::BLACK);
return write(ledValues);
}
libusb_device_handle * LedDeviceHyperionUsbasp::openDevice(libusb_device *device)
{
libusb_device_handle * handle = nullptr;
int error = libusb_open(device, &handle);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to open device(" << error << "): " << libusb_error_name(error) << std::endl;
throw error;
}
// detach kernel driver if it is active
if (libusb_kernel_driver_active(handle, 0) == 1)
{
error = libusb_detach_kernel_driver(handle, 0);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to detach kernel driver(" << error << "): " << libusb_error_name(error) << std::endl;
libusb_close(handle);
throw error;
}
}
error = libusb_claim_interface(handle, 0);
if (error != LIBUSB_SUCCESS)
{
std::cerr << "unable to claim interface(" << error << "): " << libusb_error_name(error) << std::endl;
libusb_attach_kernel_driver(handle, 0);
libusb_close(handle);
throw error;
}
return handle;
}
std::string LedDeviceHyperionUsbasp::getString(libusb_device * device, int stringDescriptorIndex)
{
libusb_device_handle * handle = nullptr;
int error = libusb_open(device, &handle);
if (error != LIBUSB_SUCCESS)
{
throw error;
}
char buffer[256];
error = libusb_get_string_descriptor_ascii(handle, stringDescriptorIndex, reinterpret_cast<unsigned char *>(buffer), sizeof(buffer));
if (error <= 0)
{
libusb_close(handle);
throw error;
}
libusb_close(handle);
return std::string(buffer, error);
}

View File

@@ -0,0 +1,88 @@
#pragma once
// stl includes
#include <vector>
#include <cstdint>
#include <string>
// libusb include
#include <libusb.h>
// Hyperion includes
#include <leddevice/LedDevice.h>
///
/// LedDevice implementation for a lightpack device (http://code.google.com/p/light-pack/)
///
class LedDeviceHyperionUsbasp : public LedDevice
{
public:
// Commands to the Device
enum Commands {
CMD_WRITE_WS2801 = 10,
CMD_WRITE_WS2812 = 11
};
///
/// Constructs the LedDeviceLightpack
///
LedDeviceHyperionUsbasp(uint8_t writeLedsCommand);
///
/// Destructor of the LedDevice; closes the output device if it is open
///
virtual ~LedDeviceHyperionUsbasp();
///
/// Opens and configures the output device
///
/// @return Zero on succes else negative
///
int open();
///
/// Writes the RGB-Color values to the leds.
///
/// @param[in] ledValues The RGB-color per led
///
/// @return Zero on success else negative
///
virtual int write(const std::vector<ColorRgb>& ledValues);
///
/// Switch the leds off
///
/// @return Zero on success else negative
///
virtual int switchOff();
private:
///
/// Test if the device is a Hyperion Usbasp device
///
/// @return Zero on succes else negative
///
int testAndOpen(libusb_device * device);
static libusb_device_handle * openDevice(libusb_device * device);
static std::string getString(libusb_device * device, int stringDescriptorIndex);
private:
/// command to write the leds
const uint8_t _writeLedsCommand;
/// libusb context
libusb_context * _libusbContext;
/// libusb device handle
libusb_device_handle * _deviceHandle;
/// Number of leds
int _ledCount;
/// Usb device identifiers
static uint16_t _usbVendorId;
static uint16_t _usbProductId;
static std::string _usbProductDescription;
};

View File

@@ -1,182 +0,0 @@
// Local hyperion includes
#include "LedDeviceWs2811.h"
ws2811::SignalTiming ws2811::fromString(const std::string& signalTiming, const SignalTiming defaultValue)
{
SignalTiming result = defaultValue;
if (signalTiming == "3755" || signalTiming == "option_3755")
{
result = option_3755;
}
else if (signalTiming == "3773" || signalTiming == "option_3773")
{
result = option_3773;
}
else if (signalTiming == "2855" || signalTiming == "option_2855")
{
result = option_2855;
}
else if (signalTiming == "2882" || signalTiming == "option_2882")
{
result = option_2882;
}
return result;
}
unsigned ws2811::getBaudrate(const SpeedMode speedMode)
{
switch (speedMode)
{
case highspeed:
// Bit length: 125ns
return 8000000;
case lowspeed:
// Bit length: 250ns
return 4000000;
}
return 0;
}
inline unsigned ws2811::getLength(const SignalTiming timing, const TimeOption option)
{
switch (timing)
{
case option_3755:
// Reference: http://www.mikrocontroller.net/attachment/180459/WS2812B_preliminary.pdf
// Unit length: 125ns
switch (option)
{
case T0H:
return 3; // 400ns +-150ns
case T0L:
return 7; // 850ns +-150ns
case T1H:
return 7; // 800ns +-150ns
case T1L:
return 3; // 450ns +-150ns
}
case option_3773:
// Reference: www.adafruit.com/datasheets/WS2812.pdf
// Unit length: 125ns
switch (option)
{
case T0H:
return 3; // 350ns +-150ns
case T0L:
return 7; // 800ns +-150ns
case T1H:
return 7; // 700ns +-150ns
case T1L:
return 3; // 600ns +-150ns
}
case option_2855:
// Reference: www.adafruit.com/datasheets/WS2811.pdf
// Unit length: 250ns
switch (option)
{
case T0H:
return 2; // 500ns +-150ns
case T0L:
return 8; // 2000ns +-150ns
case T1H:
return 5; // 1200ns +-150ns
case T1L:
return 5; // 1300ns +-150ns
}
case option_2882:
// Reference: www.szparkson.net/download/WS2811.pdf
// Unit length: 250ns
switch (option)
{
case T0H:
return 2; // 500ns +-150ns
case T0L:
return 8; // 2000ns +-150ns
case T1H:
return 8; // 2000ns +-150ns
case T1L:
return 2; // 500ns +-150ns
}
default:
std::cerr << "Unknown signal timing for ws2811: " << timing << std::endl;
}
return 0;
}
uint8_t ws2811::bitToSignal(unsigned lenHigh)
{
// Sanity check on the length of the 'high' signal
assert(0 < lenHigh && lenHigh < 10);
uint8_t result = 0x00;
for (unsigned i=1; i<lenHigh; ++i)
{
result |= (1 << (8-i));
}
return result;
}
ws2811::ByteSignal ws2811::translate(SignalTiming ledOption, uint8_t byte)
{
ByteSignal result;
result.bit_1 = bitToSignal(getLength(ledOption, (byte & 0x80)?T1H:T0H));
result.bit_2 = bitToSignal(getLength(ledOption, (byte & 0x40)?T1H:T0H));
result.bit_3 = bitToSignal(getLength(ledOption, (byte & 0x20)?T1H:T0H));
result.bit_4 = bitToSignal(getLength(ledOption, (byte & 0x10)?T1H:T0H));
result.bit_5 = bitToSignal(getLength(ledOption, (byte & 0x08)?T1H:T0H));
result.bit_6 = bitToSignal(getLength(ledOption, (byte & 0x04)?T1H:T0H));
result.bit_7 = bitToSignal(getLength(ledOption, (byte & 0x02)?T1H:T0H));
result.bit_8 = bitToSignal(getLength(ledOption, (byte & 0x01)?T1H:T0H));
return result;
}
LedDeviceWs2811::LedDeviceWs2811(
const std::string & outputDevice,
const ws2811::SignalTiming signalTiming,
const ws2811::SpeedMode speedMode) :
LedRs232Device(outputDevice, ws2811::getBaudrate(speedMode))
{
fillEncodeTable(signalTiming);
}
int LedDeviceWs2811::write(const std::vector<ColorRgb> & ledValues)
{
if (_ledBuffer.size() != ledValues.size() * 3)
{
_ledBuffer.resize(ledValues.size() * 3);
}
auto bufIt = _ledBuffer.begin();
for (const ColorRgb & color : ledValues)
{
*bufIt = _byteToSignalTable[color.red ];
++bufIt;
*bufIt = _byteToSignalTable[color.green];
++bufIt;
*bufIt = _byteToSignalTable[color.blue ];
++bufIt;
}
writeBytes(_ledBuffer.size() * 3, reinterpret_cast<uint8_t *>(_ledBuffer.data()));
return 0;
}
int LedDeviceWs2811::switchOff()
{
write(std::vector<ColorRgb>(_ledBuffer.size()/3, ColorRgb::BLACK));
return 0;
}
void LedDeviceWs2811::fillEncodeTable(const ws2811::SignalTiming ledOption)
{
_byteToSignalTable.resize(256);
for (unsigned byteValue=0; byteValue<256; ++byteValue)
{
const uint8_t byteVal = uint8_t(byteValue);
_byteToSignalTable[byteValue] = ws2811::translate(ledOption, byteVal);
}
}

View File

@@ -1,147 +0,0 @@
#pragma once
// STL includes
#include <cassert>
// Local hyperion includes
#include "LedRs232Device.h"
namespace ws2811
{
///
/// Enumaration of known signal timings
///
enum SignalTiming
{
option_3755,
option_3773,
option_2855,
option_2882,
not_a_signaltiming
};
///
/// Enumaration of the possible speeds on which the ws2811 can operate.
///
enum SpeedMode
{
lowspeed,
highspeed
};
///
/// Enumeration of the signal 'parts' (T 0 high, T 1 high, T 0 low, T 1 low).
///
enum TimeOption
{
T0H,
T1H,
T0L,
T1L
};
///
/// Structure holding the signal for a signle byte
///
struct ByteSignal
{
uint8_t bit_1;
uint8_t bit_2;
uint8_t bit_3;
uint8_t bit_4;
uint8_t bit_5;
uint8_t bit_6;
uint8_t bit_7;
uint8_t bit_8;
};
// Make sure the structure is exatly the length we require
static_assert(sizeof(ByteSignal) == 8, "Incorrect sizeof ByteSignal (expected 8)");
///
/// Translates a string to a signal timing
///
/// @param signalTiming The string specifying the signal timing
/// @param defaultValue The default value (used if the string does not match any known timing)
///
/// @return The SignalTiming (or not_a_signaltiming if it did not match)
///
SignalTiming fromString(const std::string& signalTiming, const SignalTiming defaultValue);
///
/// Returns the required baudrate for a specific signal-timing
///
/// @param SpeedMode The WS2811/WS2812 speed mode (WS2812b only has highspeed)
///
/// @return The required baudrate for the signal timing
///
unsigned getBaudrate(const SpeedMode speedMode);
///
/// The number of 'signal units' (bits) For the subpart of a specific timing scheme
///
/// @param timing The controller option
/// @param option The signal part
///
unsigned getLength(const SignalTiming timing, const TimeOption option);
///
/// Constructs a 'bit' based signal with defined 'high' length (and implicite defined 'low'
/// length. The signal is based on a 10bits bytes (incl. high startbit and low stopbit). The
/// total length of the high is given as parameter:<br>
/// lenHigh=7 => |-------|___| => 1 1111 1100 0 => 252 (start and stop bit are implicite)
///
/// @param lenHigh The total length of the 'high' length (incl start-bit)
/// @return The byte representing the high-low signal
///
uint8_t bitToSignal(unsigned lenHigh);
///
/// Translate a byte into signal levels for a specific WS2811 option
///
/// @param ledOption The WS2811 configuration
/// @param byte The byte to translate
///
/// @return The signal for the given byte (one byte per bit)
///
ByteSignal translate(SignalTiming ledOption, uint8_t byte);
}
class LedDeviceWs2811 : public LedRs232Device
{
public:
///
/// Constructs the LedDevice with Ws2811 attached via a serial port
///
/// @param outputDevice The name of the output device (eg '/dev/ttyS0')
/// @param signalTiming The timing scheme used by the Ws2811 chip
/// @param speedMode The speed modus of the Ws2811 chip
///
LedDeviceWs2811(const std::string& outputDevice, const ws2811::SignalTiming signalTiming, const ws2811::SpeedMode speedMode);
///
/// Writes the led color values to the led-device
///
/// @param ledValues The color-value per led
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues);
/// Switch the leds off
virtual int switchOff();
private:
///
/// Fill the byte encoding table (_byteToSignalTable) for the specific timing option
///
/// @param ledOption The timing option
///
void fillEncodeTable(const ws2811::SignalTiming ledOption);
/// Translation table of byte to signal///
std::vector<ws2811::ByteSignal> _byteToSignalTable;
/// The buffer containing the packed RGB values
std::vector<ws2811::ByteSignal> _ledBuffer;
};

View File

@@ -1,96 +0,0 @@
// Linux includes
#include <unistd.h>
// Local Hyperion-Leddevice includes
#include "LedDeviceWs2812b.h"
LedDeviceWs2812b::LedDeviceWs2812b() :
LedRs232Device("/dev/ttyUSB0", 2000000)
{
// empty
}
int LedDeviceWs2812b::write(const std::vector<ColorRgb> & ledValues)
{
// Ensure the size of the led-buffer
if (_ledBuffer.size() != ledValues.size()*8)
{
_ledBuffer.resize(ledValues.size()*8, ~0x24);
}
// Translate the channel of each color to a signal
uint8_t * signal_ptr = _ledBuffer.data();
for (const ColorRgb & color : ledValues)
{
signal_ptr = color2signal(color, signal_ptr);
}
const int result = writeBytes(_ledBuffer.size(), _ledBuffer.data());
// Official latch time is 50us (lets give it 50us more)
usleep(100);
return result;
}
uint8_t * LedDeviceWs2812b::color2signal(const ColorRgb & color, uint8_t * signal)
{
*signal = bits2Signal(color.red & 0x80, color.red & 0x40, color.red & 0x20);
++signal;
*signal = bits2Signal(color.red & 0x10, color.red & 0x08, color.red & 0x04);
++signal;
*signal = bits2Signal(color.red & 0x02, color.green & 0x01, color.green & 0x80);
++signal;
*signal = bits2Signal(color.green & 0x40, color.green & 0x20, color.green & 0x10);
++signal;
*signal = bits2Signal(color.green & 0x08, color.green & 0x04, color.green & 0x02);
++signal;
*signal = bits2Signal(color.green & 0x01, color.blue & 0x80, color.blue & 0x40);
++signal;
*signal = bits2Signal(color.blue & 0x20, color.blue & 0x10, color.blue & 0x08);
++signal;
*signal = bits2Signal(color.blue & 0x04, color.blue & 0x02, color.blue & 0x01);
++signal;
return signal;
}
int LedDeviceWs2812b::switchOff()
{
// Set all bytes in the signal buffer to zero
for (uint8_t & signal : _ledBuffer)
{
signal = ~0x24;
}
return writeBytes(_ledBuffer.size(), _ledBuffer.data());
}
uint8_t LedDeviceWs2812b::bits2Signal(const bool bit_1, const bool bit_2, const bool bit_3) const
{
// See https://github.com/tvdzwan/hyperion/wiki/Ws2812b for the explanation of the given
// translations
// Bit index(default):1 2 3
// | | |
// default value (1) 00 100 10 (0)
//
// Reversed value (1) 01 001 00 (0)
// | | |
// Bit index (rev): 3 2 1
uint8_t result = 0x24;
if(bit_1)
{
result |= 0x01;
}
if (bit_2)
{
result |= 0x08;
}
if (bit_3)
{
result |= 0x40;
}
return ~result;
}

View File

@@ -1,60 +0,0 @@
#pragma once
// Hyperion leddevice includes
#include "LedRs232Device.h"
///
/// The LedDevice for controlling a string of WS2812B leds. These are controlled over the mini-UART
/// of the RPi (/dev/ttyAMA0).
///
class LedDeviceWs2812b : public LedRs232Device
{
public:
///
/// Constructs the device (all required parameters are hardcoded)
///
LedDeviceWs2812b();
///
/// Write the color data the the WS2812B led string
///
/// @param ledValues The color data
/// @return Zero on succes else negative
///
virtual int write(const std::vector<ColorRgb> & ledValues);
///
/// Write zero to all leds(that have been written by a previous write operation)
///
/// @return Zero on succes else negative
///
virtual int switchOff();
private:
///
/// Translate a color to the signal bits. The resulting bits are written to the given memory.
///
/// @param color The color to translate
/// @param signal The pointer at the beginning of the signal to write
/// @return The pointer at the end of the written signal
///
uint8_t * color2signal(const ColorRgb & color, uint8_t * signal);
///
/// Translates three bits to a single byte
///
/// @param bit1 The value of the first bit (1=true, zero=false)
/// @param bit2 The value of the second bit (1=true, zero=false)
/// @param bit3 The value of the third bit (1=true, zero=false)
///
/// @return The output-byte for the given two bit
///
uint8_t bits2Signal(const bool bit1, const bool bit2, const bool bit3) const;
///
/// The output buffer for writing bytes to the output
///
std::vector<uint8_t> _ledBuffer;
};