diff --git a/bin/install_hyperion.sh b/bin/install_hyperion.sh index 4d3e9a2c..4c06e696 100755 --- a/bin/install_hyperion.sh +++ b/bin/install_hyperion.sh @@ -40,7 +40,7 @@ if [ $IS_OPENELEC -eq 1 ]; then if [ $IS_IMX6 -eq 1 ]; then curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion_imx6.tar.gz | tar -C /storage -xz else - curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz | tar -C /storage -xz + curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion_rpi.tar.gz | tar -C /storage -xz fi curl -L --get https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.deps.openelec-rpi.tar.gz | tar -C /storage/hyperion/bin -xz # modify the default config to have a correct effect path @@ -49,7 +49,7 @@ else if [ $IS_IMX6 -eq 1 ]; then wget https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion_imx6.tar.gz -O - | tar -C /opt -xz else - wget https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion.tar.gz -O - | tar -C /opt -xz + wget https://raw.githubusercontent.com/tvdzwan/hyperion/master/deploy/hyperion_rpi.tar.gz -O - | tar -C /opt -xz fi fi diff --git a/doc/UDP_led_driver.txt b/doc/UDP_led_driver.txt new file mode 100644 index 00000000..646abe21 --- /dev/null +++ b/doc/UDP_led_driver.txt @@ -0,0 +1,57 @@ +BACKGROUND +--------------------------------------------------------- +The UDP led device type can be used to send LED data over UDP packets. +It was originally designed to support an ESP8266 Wifi module based WS2812 +LED strip controller. + +I've used this to support : +- A string of 600 LEDs as xmas decorations + The effects development kit is great for these scenarios + +- a 61 LED collection of concentric circles + This has been used as a "night light" and a super lo-res + TV + +In each of these cases, the hyperion-remote iOS app is a great way to +control the effects. + + +CONFIG +--------------------------------------------------------- +Simple example for devices that support a raw binary protocol. + "device" : + { + "name" : "MyPi", + "type" : "udp", + "output" : "esp201-0.home:2391", "protocol" : 0, + "rate" : 1000000, + "colorOrder" : "grb" + }, + + + +If you are using an ESP8266/Arduino device with a long LED strip, you chould use this alternate protocol. +The ESP8266/Arduino doesnt support datagram re-assembly so will never see any udp packets greater than 1450. + "device" : + { + "name" : "MyPi", + "type" : "udp", +// "output" : "esp201-0.home:2392", "protocol" : 2, "maxpacket" : 1450, + "rate" : 1000000, + "colorOrder" : "rgb" + }, + +PROTOCOL +--------------------------------------------------------- +Simple UDP packets are sent. + +Packet Format protocol 0: +3 bytes per LED as R, G, B + +Packet Format protocol 2: +0: update number & 0xf; +1: fragment of this update +2: 1st led# of this update - high byte +3: 1st led# of this update - low byte +4..n 3 bytes per LED as R, G, B + diff --git a/effects/udp.json b/effects/udp.json new file mode 100644 index 00000000..defd4f89 --- /dev/null +++ b/effects/udp.json @@ -0,0 +1,8 @@ +{ + "name" : "UDP listener", + "script" : "udp.py", + "args" : + { + "udpPort" : 2391 + } +} diff --git a/effects/udp.py b/effects/udp.py new file mode 100644 index 00000000..f8317a20 --- /dev/null +++ b/effects/udp.py @@ -0,0 +1,47 @@ +import hyperion +import time +import colorsys +import socket +import errno + +# Get the parameters +udpPort = int(hyperion.args.get('udpPort', 2812)) + +UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) +UDPSock.setblocking(False) + +listen_addr = ("",udpPort) +print "udp.py: bind socket port:",udpPort +UDPSock.bind(listen_addr) + +hyperion.setColor(hyperion.ledCount * bytearray((int(0), int(0), int(0))) ) + +# Start the write data loop +while not hyperion.abort(): + try: + data,addr = UDPSock.recvfrom(4500) +# print data.strip(),len(data),addr + if (len(data)%3 == 0): +# print "numleds ",len(data)/3 + ledData = bytearray() + for i in range(hyperion.ledCount): + if (i<(len(data)/3)): + ledData += data[i*3+0] + ledData += data[i*3+1] + ledData += data[i*3+2] + else: + ledData += bytearray((int(0), int(0), int(0))) + + hyperion.setColor(ledData) + + else: + print "not div 3" + except IOError as e: + if e.errno == errno.EWOULDBLOCK: + pass + else: + print "errno:", e.errno + +print "udp.py: closing socket" +UDPSock.close() + diff --git a/libsrc/leddevice/CMakeLists.txt b/libsrc/leddevice/CMakeLists.txt index 96098ed3..15d2801d 100755 --- a/libsrc/leddevice/CMakeLists.txt +++ b/libsrc/leddevice/CMakeLists.txt @@ -33,8 +33,9 @@ SET(Leddevice_HEADERS ${CURRENT_SOURCE_DIR}/LedDevicePaintpack.h ${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.h ${CURRENT_SOURCE_DIR}/LedDeviceSedu.h - ${CURRENT_SOURCE_DIR}/LedDeviceTest.h - ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h + ${CURRENT_SOURCE_DIR}/LedDeviceTest.h + ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.h + ${CURRENT_SOURCE_DIR}/LedDeviceUdp.h ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.h ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.h ${CURRENT_SOURCE_DIR}/LedDeviceAtmo.h @@ -55,8 +56,9 @@ SET(Leddevice_SOURCES ${CURRENT_SOURCE_DIR}/LedDevicePaintpack.cpp ${CURRENT_SOURCE_DIR}/LedDevicePiBlaster.cpp ${CURRENT_SOURCE_DIR}/LedDeviceSedu.cpp - ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp - ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceTest.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceFadeCandy.cpp + ${CURRENT_SOURCE_DIR}/LedDeviceUdp.cpp ${CURRENT_SOURCE_DIR}/LedDeviceHyperionUsbasp.cpp ${CURRENT_SOURCE_DIR}/LedDevicePhilipsHue.cpp ${CURRENT_SOURCE_DIR}/LedDeviceTpm2.cpp diff --git a/libsrc/leddevice/LedDeviceFactory.cpp b/libsrc/leddevice/LedDeviceFactory.cpp index f692b03c..4581a950 100755 --- a/libsrc/leddevice/LedDeviceFactory.cpp +++ b/libsrc/leddevice/LedDeviceFactory.cpp @@ -31,6 +31,7 @@ #include "LedDeviceSedu.h" #include "LedDeviceTest.h" #include "LedDeviceFadeCandy.h" +#include "LedDeviceUdp.h" #include "LedDeviceHyperionUsbasp.h" #include "LedDevicePhilipsHue.h" #include "LedDeviceTpm2.h" @@ -251,6 +252,14 @@ LedDevice * LedDeviceFactory::construct(const Json::Value & deviceConfig) const uint16_t channel = deviceConfig.get("channel", 0).asInt(); device = new LedDeviceFadeCandy(host, port, channel); } + else if (type == "udp") + { + const std::string output = deviceConfig["output"].asString(); + const unsigned rate = deviceConfig["rate"].asInt(); + const unsigned protocol = deviceConfig["protocol"].asInt(); + const unsigned maxPacket = deviceConfig["maxpacket"].asInt(); + device = new LedDeviceUdp(output, rate, protocol, maxPacket); + } else if (type == "tpm2") { const std::string output = deviceConfig["output"].asString(); diff --git a/libsrc/leddevice/LedDeviceFadeCandy.cpp b/libsrc/leddevice/LedDeviceFadeCandy.cpp index 685795a9..600547c7 100755 --- a/libsrc/leddevice/LedDeviceFadeCandy.cpp +++ b/libsrc/leddevice/LedDeviceFadeCandy.cpp @@ -1,8 +1,8 @@ #include "LedDeviceFadeCandy.h" -static const unsigned MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds -static const unsigned OPC_SET_PIXELS = 0; // OPC command codes -static const unsigned OPC_HEADER_SIZE = 4; // OPC header size +static const ssize_t MAX_NUM_LEDS = 10000; // OPC can handle 21845 leds - in theory, fadecandy device should handle 10000 leds +static const ssize_t OPC_SET_PIXELS = 0; // OPC command codes +static const ssize_t OPC_HEADER_SIZE = 4; // OPC header size LedDeviceFadeCandy::LedDeviceFadeCandy(const std::string& host, const uint16_t port, const unsigned channel) : diff --git a/libsrc/leddevice/LedDeviceUdp.cpp b/libsrc/leddevice/LedDeviceUdp.cpp new file mode 100644 index 00000000..129eb47f --- /dev/null +++ b/libsrc/leddevice/LedDeviceUdp.cpp @@ -0,0 +1,165 @@ + +// Local-Hyperion includes +#include "LedDeviceUdp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct addrinfo hints, *servinfo, *p; +//char udpbuffer[1024]; +int sockfd; +int ledprotocol; +int leds_per_pkt; +int update_number; +int fragment_number; + +LedDeviceUdp::LedDeviceUdp(const std::string& output, const unsigned baudrate, const unsigned protocol, const unsigned maxPacket) +//LedDeviceUdp::LedDeviceUdp(const std::string& output, const unsigned baudrate) : +// _ofs(output.empty()?"/home/pi/LedDevice.out":output.c_str()) +{ + + std::string hostname; + std::string port; + ledprotocol = protocol; + leds_per_pkt = ((maxPacket-4)/3); + if (leds_per_pkt <= 0) { + leds_per_pkt = 200; + } + +//printf ("leds_per_pkt is %d\n", leds_per_pkt); + int got_colon=0; + for (unsigned int i=0; iai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("talker: socket"); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "talker: failed to create socket\n"); + assert(p!=NULL); + } +} + +LedDeviceUdp::~LedDeviceUdp() +{ + // empty +} + +int LedDeviceUdp::write(const std::vector & ledValues) +{ + + char udpbuffer[4096]; + int udpPtr=0; + + update_number++; + update_number &= 0xf; + + if (ledprotocol == 0) { + int i=0; + for (const ColorRgb& color : ledValues) + { + if (i<4090) { + udpbuffer[i++] = color.red; + udpbuffer[i++] = color.green; + udpbuffer[i++] = color.blue; + } + //printf ("c.red %d sz c.red %d\n", color.red, sizeof(color.red)); + } + sendto(sockfd, udpbuffer, i, 0, p->ai_addr, p->ai_addrlen); + } + if (ledprotocol == 1) { +#define MAXLEDperFRAG 450 + int mLedCount = ledValues.size(); + + for (int frag=0; frag<4; frag++) { + udpPtr=0; + udpbuffer[udpPtr++] = 0; + udpbuffer[udpPtr++] = 0; + udpbuffer[udpPtr++] = (frag*MAXLEDperFRAG)/256; // high byte + udpbuffer[udpPtr++] = (frag*MAXLEDperFRAG)%256; // low byte + int ct=0; + for (int this_led = frag*300; ((this_led 7) + sendto(sockfd, udpbuffer, udpPtr, 0, p->ai_addr, p->ai_addrlen); + } + } + if (ledprotocol == 2) { + udpPtr = 0; + unsigned int ledCtr = 0; + fragment_number = 0; + udpbuffer[udpPtr++] = update_number & 0xf; + udpbuffer[udpPtr++] = fragment_number++; + udpbuffer[udpPtr++] = ledCtr/256; // high byte + udpbuffer[udpPtr++] = ledCtr%256; // low byte + + for (const ColorRgb& color : ledValues) + { + if (udpPtr<4090) { + udpbuffer[udpPtr++] = color.red; + udpbuffer[udpPtr++] = color.green; + udpbuffer[udpPtr++] = color.blue; + } + ledCtr++; + if ( (ledCtr % leds_per_pkt == 0) || (ledCtr == ledValues.size()) ) { + sendto(sockfd, udpbuffer, udpPtr, 0, p->ai_addr, p->ai_addrlen); + memset(udpbuffer, 0, sizeof udpbuffer); + udpPtr = 0; + udpbuffer[udpPtr++] = update_number & 0xf; + udpbuffer[udpPtr++] = fragment_number++; + udpbuffer[udpPtr++] = ledCtr/256; // high byte + udpbuffer[udpPtr++] = ledCtr%256; // low byte + } + } + + } + return 0; +} + +int LedDeviceUdp::switchOff() +{ +// return write(std::vector(mLedCount, ColorRgb{0,0,0})); + return 0; +} diff --git a/libsrc/leddevice/LedDeviceUdp.h b/libsrc/leddevice/LedDeviceUdp.h new file mode 100644 index 00000000..c8e2f119 --- /dev/null +++ b/libsrc/leddevice/LedDeviceUdp.h @@ -0,0 +1,44 @@ +#pragma once + +// STL includes0 +#include + +// Leddevice includes +#include + +/// +/// Implementation of the LedDevice that write the led-colors to an +/// ASCII-textfile('/home/pi/LedDevice.out') +/// +class LedDeviceUdp : public LedDevice +{ +public: + /// + /// Constructs the test-device, which opens an output stream to the file + /// + LedDeviceUdp(const std::string& output, const unsigned baudrate, const unsigned protocol, const unsigned maxPacket); + + /// + /// Destructor of this test-device + /// + virtual ~LedDeviceUdp(); + + /// + /// Writes the given led-color values to the output stream + /// + /// @param ledValues The color-value per led + /// + /// @return Zero on success else negative + /// + virtual int write(const std::vector & ledValues); + + /// Switch the leds off + virtual int switchOff(); + +private: + /// The outputstream +// std::ofstream _ofs; + + /// the number of leds (needed when switching off) + size_t mLedCount; +};