From dd91621ccee033550312683293b5bf40c3599053 Mon Sep 17 00:00:00 2001 From: cbdev Date: Sun, 23 Feb 2020 18:20:12 +0100 Subject: Implement OpenPixelControl output --- backends/openpixelcontrol.md | 49 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 backends/openpixelcontrol.md (limited to 'backends/openpixelcontrol.md') diff --git a/backends/openpixelcontrol.md b/backends/openpixelcontrol.md new file mode 100644 index 0000000..ce60278 --- /dev/null +++ b/backends/openpixelcontrol.md @@ -0,0 +1,49 @@ +### The `openpixelcontrol` backend + +This backend provides read-write access to the TCP-based OpenPixelControl protocol, +used for controlling intelligent RGB led strips. + +This backend can both control a remote OpenPixelControl server as well as receive data +from OpenPixelControl clients. + +#### Global configuration + +This backend does not take any global configuration. + +#### Instance configuration + +| Option | Example value | Default value | Description | +|---------------|-----------------------|-----------------------|-----------------------| +| `destination` | `10.11.12.1 9001` | none | Destination for output data. Setting this option enables the instance for output | +| `listen` | `10.11.12.2 9002` | none | Local address to wait for client connections on. Setting this enables the instance for input | +| `mode` | `16bit` | `8bit` | RGB channel resolution | + +#### Channel specification + +Each instance can control up to 255 strips of RGB LED lights. The OpenPixelControl specification +confusingly calls these strips "channels". + +Strip `0` acts as a "broadcast" strip, setting values on all other strips at once. +Consequently, components on strip 0 can only be mapped as output channels to a destination +(setting components on all strips there), not as input channels. When such messages are received from +a client, the corresponding mapped component channels on all strips will receive events. + +Every single component of any LED on any string can be mapped as an individual MIDIMonster channel. +The components are laid out as sequences of Red - Green - Blue value triplets. + +Channels can be specified by their sequential index (one-based). + +Example mapping (data from Strip 2 LED 66's green component is mapped to the blue component of LED 2 on strip 1): +``` +strip1.channel6 < strip2.channel200 +``` + +Additionally, channels may be referred to by their color component and LED index: +``` +strip1.blue2 < strip2.green66 +``` + +#### Known bugs / problems + +If the connection is lost, it is currently not reestablished and may cause exit the MIDIMonster entirely. +Thisi behaviour may be changed in future releases. -- cgit v1.2.3 From e1fcd4d11cfdbad54470b2cce98d8b749464ec00 Mon Sep 17 00:00:00 2001 From: cbdev Date: Thu, 27 Feb 2020 00:33:22 +0100 Subject: Implement OpenPixelControl server mode (8bit) --- README.md | 6 +- backends/libmmbackend.c | 4 + backends/openpixelcontrol.c | 220 +++++++++++++++++++++++++++++++++++++++++-- backends/openpixelcontrol.h | 15 ++- backends/openpixelcontrol.md | 9 +- 5 files changed, 241 insertions(+), 13 deletions(-) (limited to 'backends/openpixelcontrol.md') diff --git a/README.md b/README.md index 46331f3..4d0b052 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ MIDIMonster Logo -[![Build Status](https://travis-ci.com/cbdevnet/midimonster.svg?branch=master)](https://travis-ci.com/cbdevnet/midimonster) [![Coverity Scan Build Status](https://scan.coverity.com/projects/15168/badge.svg)](https://scan.coverity.com/projects/15168) +[![Build Status](https://travis-ci.com/cbdevnet/midimonster.svg?branch=master)](https://travis-ci.com/cbdevnet/midimonster) +[![Coverity Scan Build Status](https://scan.coverity.com/projects/15168/badge.svg)](https://scan.coverity.com/projects/15168) +[![IRC Channel](https://static.midimonster.net/hackint-badge.svg)](https://webirc.hackint.org/#irc://irc.hackint.org/#midimonster) Named for its scary math, the MIDIMonster is a universal control and translation tool for multi-channel absolute-value-based control and/or bus protocols. @@ -15,6 +17,7 @@ Currently, the MIDIMonster supports the following protocols: | ArtNet | Linux, Windows, OSX | Version 4 | [`artnet`](backends/artnet.md)| | Streaming ACN (sACN / E1.31) | Linux, Windows, OSX | | [`sacn`](backends/sacn.md) | | OpenSoundControl (OSC) | Linux, Windows, OSX | | [`osc`](backends/osc.md) | +| OpenPixelControl | Linux, Windows, OSX | 8 Bit & 16 Bit modes | [`openpixelcontrol`](backends/openpixelcontrol.md) | | evdev input devices | Linux | Virtual output supported | [`evdev`](backends/evdev.md) | | Open Lighting Architecture | Linux, OSX | | [`ola`](backends/ola.md) | | MA Lighting Web Remote | Linux, Windows, OSX | GrandMA2 and dot2 (incl. OnPC) | [`maweb`](backends/maweb.md) | @@ -142,6 +145,7 @@ special information. These documentation files are located in the `backends/` di * [`loopback` backend documentation](backends/loopback.md) * [`ola` backend documentation](backends/ola.md) * [`osc` backend documentation](backends/osc.md) +* [`openpixelcontrol` backend documentation](backends/openpixelcontrol.md) * [`lua` backend documentation](backends/lua.md) * [`maweb` backend documentation](backends/maweb.md) diff --git a/backends/libmmbackend.c b/backends/libmmbackend.c index ffa403b..b9513ac 100644 --- a/backends/libmmbackend.c +++ b/backends/libmmbackend.c @@ -153,7 +153,11 @@ int mmbackend_socket(char* host, char* port, int socktype, uint8_t listener, uin int mmbackend_send(int fd, uint8_t* data, size_t length){ ssize_t total = 0, sent; while(total < length){ + #ifndef LIBMMBACKEND_TCP_TORTURE sent = send(fd, data + total, length - total, 0); + #else + sent = send(fd, data + total, 1, 0); + #endif if(sent < 0){ LOGPF("Failed to send: %s", strerror(errno)); return 1; diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 062c015..3a94a12 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -52,6 +52,7 @@ static int openpixel_configure_instance(instance* inst, char* option, char* valu if(data->dest_fd >= 0){ return 0; } + LOGPF("Failed to connect to server for instance %s", inst->name); return 1; } if(!strcmp(option, "listen")){ @@ -62,9 +63,10 @@ static int openpixel_configure_instance(instance* inst, char* option, char* valu } data->listen_fd = mmbackend_socket(host, port, SOCK_STREAM, 1, 0); - if(data->listen_fd >= 0 && listen(data->listen_fd, SOMAXCONN)){ + if(data->listen_fd >= 0 && !listen(data->listen_fd, SOMAXCONN)){ return 0; } + LOGPF("Failed to bind server descriptor for instance %s", inst->name); return 1; } else if(!strcmp(option, "mode")){ @@ -103,15 +105,22 @@ static ssize_t openpixel_buffer_find(openpixel_instance_data* data, uint8_t stri for(n = 0; n < data->buffers; n++){ if(data->buffer[n].strip == strip && (data->buffer[n].flags & OPENPIXEL_INPUT) >= input){ + DBGPF("Using allocated %s buffer for requested strip %d, size %d", input ? "input" : "output", strip, data->buffer[n].bytes); return n; } } + DBGPF("Instance has no %s buffer for requested strip %d", input ? "input" : "output", strip); return -1; } -static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint8_t length){ +static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint16_t length){ ssize_t buffer = openpixel_buffer_find(data, strip, input); + + //length is in component-channels, round it to the nearest rgb-triplet + //this guarantees that any allocated buffer has at least three bytes, which is important to parts of the receive handler length = (length % 3) ? ((length / 3) + 1) * 3 : length; + + //calculate required buffer length size_t bytes_required = (data->mode == rgb8) ? length : length * 2; if(buffer < 0){ //allocate new buffer @@ -242,7 +251,7 @@ static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* } if(strip == 0){ - //update values in all other output strips, dont mark + //update values in all other output strips, don't mark for(p = 0; p < data->buffers; p++){ if(!(data->buffer[p].flags & OPENPIXEL_INPUT)){ //check whether the buffer is large enough @@ -278,8 +287,204 @@ static int openpixel_set(instance* inst, size_t num, channel** c, channel_value* return 0; } +static int openpixel_client_new(instance* inst, int fd){ + if(fd < 0){ + return 1; + } + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + size_t u; + + //mark nonblocking + #ifdef _WIN32 + unsigned long flags = 1; + if(ioctlsocket(fd, FIONBIO, &flags)){ + #else + int flags = fcntl(fd, F_GETFL, 0); + if(fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0){ + #endif + LOGPF("Failed to set client descriptor on %s nonblocking", inst->name); + close(fd); + return 0; + } + + //find a client block + for(u = 0; u < data->clients; u++){ + if(data->client[u].fd <= 0){ + break; + } + } + + //if no free slot, make one + if(u == data->clients){ + data->client = realloc(data->client, (data->clients + 1) * sizeof(openpixel_client)); + if(!data->client){ + data->clients = 0; + LOG("Failed to allocate memory"); + return 1; + } + data->clients++; + } + + data->client[u].fd = fd; + data->client[u].buffer = -1; + data->client[u].offset = 0; + + return mm_manage_fd(fd, BACKEND_NAME, 1, inst); +} + +static int openpixel_client_handle(instance* inst, int fd){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + uint8_t buffer[8192]; + size_t c = 0, offset = 0, u; + ssize_t bytes_left = 0; + channel* chan = NULL; + channel_value val; + + for(c = 0; c < data->clients; c++){ + if(data->client[c].fd == fd){ + break; + } + } + + if(c == data->clients){ + LOGPF("Unknown client descriptor signaled on %s", inst->name); + return 1; + } + + //FIXME might want to read until EAGAIN + ssize_t bytes = recv(fd, buffer, sizeof(buffer), 0); + if(bytes <= 0){ + if(bytes < 0){ + LOGPF("Failed to receive from client: %s", strerror(errno)); + } + + //close the connection + close(fd); + data->client[c].fd = -1; + //unmanage the fd + mm_manage_fd(fd, BACKEND_NAME, 0, NULL); + return 0; + } + + for(bytes_left = bytes - offset; bytes_left > 0; bytes_left = bytes - offset){ + if(data->client[c].buffer == -1){ + //read a header + DBGPF("Reading %" PRIsize_t " bytes to header at offset %" PRIsize_t ", header size %" PRIsize_t ", %" PRIsize_t " bytes left", min(sizeof(openpixel_header) - data->client[c].offset, bytes_left), data->client[c].offset, sizeof(openpixel_header), bytes_left); + memcpy(((uint8_t*) (&data->client[c].hdr)) + data->client[c].offset, buffer + offset, min(sizeof(openpixel_header) - data->client[c].offset, bytes_left)); + + //if done, resolve buffer + if(sizeof(openpixel_header) - data->client[c].offset < bytes_left){ + data->client[c].buffer = openpixel_buffer_find(data, data->client[c].hdr.strip, 1); + //if no buffer or mode mismatch, ignore data + if(data->client[c].buffer < 0 + || data->mode != data->client[c].hdr.mode){ + data->client[c].buffer = -2; //mark for ignore + } + data->client[c].left = be16toh(data->client[c].hdr.length); + data->client[c].offset = 0; + } + //if not, update client offset + else{ + data->client[c].offset += bytes_left; + } + + //update scan offset + offset += min(sizeof(openpixel_header) - data->client[c].offset, bytes_left); + } + else{ + //read data + if(data->client[c].buffer == -2){ + //ignore data + offset += min(data->client[c].left, bytes_left); + data->client[c].offset += min(data->client[c].left, bytes_left); + data->client[c].left -= min(data->client[c].left, bytes_left); + } + else{ + if(data->mode == rgb8){ + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + data->client[c].offset >= data->buffer[data->client[c].buffer].bytes){ + data->client[c].buffer = -2; + break; + } + + //FIXME if at start of trailing non-multiple of 3, ignore + + //update changed channels + if(data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] != buffer[offset + u]){ + data->buffer[data->client[c].buffer].data.u8[u + data->client[c].offset] = buffer[offset + u]; + chan = mm_channel(inst, ((uint64_t) data->client[c].hdr.strip << 32) | (u + data->client[c].offset + 1), 0); + if(chan){ + //push event + val.raw.u64 = buffer[offset + u]; + val.normalised = (double) buffer[offset + u] / 255.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + //FIXME err out here + } + } + } + } + + //update offsets + offset += u; + data->client[c].offset += u; + data->client[c].left -= u; + } + else{ + //TODO byte-order conversion may be on recv boundary + //if over buffer length, ignore + //skip non-multiple-of 6 trailing data + } + } + + //end of data, return to reading headers + if(data->client[c].left == 0){ + data->client[c].buffer = -1; + data->client[c].offset = 0; + data->client[c].left = 0; + } + } + } + + return 0; +} + static int openpixel_handle(size_t num, managed_fd* fds){ - //TODO handle bcast + size_t u; + instance* inst = NULL; + openpixel_instance_data* data = NULL; + uint8_t buffer[8192]; + ssize_t bytes; + + for(u = 0; u < num; u++){ + inst = (instance*) fds[u].impl; + data = (openpixel_instance_data*) inst->impl; + + if(fds[u].fd == data->dest_fd){ + //destination fd ready to read + //since the protocol does not define any responses, the connection was probably closed + bytes = recv(data->dest_fd, buffer, sizeof(buffer), 0); + if(bytes <= 0){ + LOGPF("Output descriptor closed on instance %s", inst->name); + //unmanage the fd to give the core some rest + mm_manage_fd(data->dest_fd, BACKEND_NAME, 0, NULL); + } + else{ + LOGPF("Unhandled response data on %s (%" PRIsize_t" bytes)", inst->name, bytes); + } + } + else if(fds[u].fd == data->listen_fd){ + //listen fd ready to read, accept a new client + if(openpixel_client_new(inst, accept(data->listen_fd, NULL, NULL))){ + return 1; + } + } + else{ + //handle client input + openpixel_client_handle(inst, fds[u].fd); + } + } return 0; } @@ -323,12 +528,11 @@ static int openpixel_shutdown(size_t n, instance** inst){ //shutdown all clients for(p = 0; p < data->clients; p++){ - if(data->client_fd[p] >= 0){ - close(data->client_fd[p]); + if(data->client[p].fd>= 0){ + close(data->client[p].fd); } } - free(data->client_fd); - free(data->bytes_left); + free(data->client); //close all configured fds if(data->listen_fd >= 0){ diff --git a/backends/openpixelcontrol.h b/backends/openpixelcontrol.h index f1061ea..658bbf0 100644 --- a/backends/openpixelcontrol.h +++ b/backends/openpixelcontrol.h @@ -31,6 +31,18 @@ typedef struct /*_openpixel_hdr*/ { } openpixel_header; #pragma pack(pop) +typedef struct /*_openpixel_client*/ { + int fd; + ssize_t buffer; + openpixel_header hdr; + size_t offset; + size_t left; + union { + uint8_t u8[2]; + uint16_t u16; + } boundary; +} openpixel_client; + typedef struct { enum { rgb8 = 0, @@ -43,6 +55,5 @@ typedef struct { int dest_fd; int listen_fd; size_t clients; - int* client_fd; - size_t* bytes_left; + openpixel_client* client; } openpixel_instance_data; diff --git a/backends/openpixelcontrol.md b/backends/openpixelcontrol.md index ce60278..6dd38bc 100644 --- a/backends/openpixelcontrol.md +++ b/backends/openpixelcontrol.md @@ -45,5 +45,10 @@ strip1.blue2 < strip2.green66 #### Known bugs / problems -If the connection is lost, it is currently not reestablished and may cause exit the MIDIMonster entirely. -Thisi behaviour may be changed in future releases. +If the connection is lost, it is currently not reestablished and may cause the MIDIMonster to exit entirely. +This behaviour may be changed in future releases. + +While acting as an OpenPixelControl server, the backend allows multiple clients to connect. +This may lead to confusing data output when multiple clients are trying to control the same strip. + +16 bit server mode is not implemented yet. This will be fixed in a future release. -- cgit v1.2.3 From 454e757f740b74d714e9fa47bfa4954cb30e67ba Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 28 Feb 2020 21:10:45 +0100 Subject: Implement openpixelcontrol 16bit server mode --- backends/openpixelcontrol.c | 35 ++++++++++++++++++++++++++++++++--- backends/openpixelcontrol.md | 2 -- 2 files changed, 32 insertions(+), 5 deletions(-) (limited to 'backends/openpixelcontrol.md') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 0f590c6..62cdb68 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -385,9 +385,38 @@ static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* clie return u; } else{ - //TODO byte-order conversion may be on recv boundary - //if over buffer length, ignore - //skip non-multiple-of 6 trailing data + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= data->buffer[client->buffer].bytes){ + client->buffer = -2; + break; + } + + //if at start of trailing non-multiple of 6, ignore + if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ + client->buffer = -2; + break; + } + + //byte-order conversion may be on message boundary, do it via a buffer + client->boundary.u8[(client->offset + u) % 2] = buffer[u]; + + //detect and update changed channels + if((client->offset + u) % 2 + && data->buffer[client->buffer].data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ + data->buffer[client->buffer].data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); + chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | ((u + client->offset) / 2 + 1), 0); + if(chan){ + //push event + val.raw.u64 = be16toh(client->boundary.u16);; + val.normalised = (double) val.raw.u64 / 65535.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + + } + } } } return -1; diff --git a/backends/openpixelcontrol.md b/backends/openpixelcontrol.md index 6dd38bc..5a8686f 100644 --- a/backends/openpixelcontrol.md +++ b/backends/openpixelcontrol.md @@ -50,5 +50,3 @@ This behaviour may be changed in future releases. While acting as an OpenPixelControl server, the backend allows multiple clients to connect. This may lead to confusing data output when multiple clients are trying to control the same strip. - -16 bit server mode is not implemented yet. This will be fixed in a future release. -- cgit v1.2.3 From 1b3f7610d47ba5464e7aa2e16b5b1a27b7d0f5a3 Mon Sep 17 00:00:00 2001 From: cbdev Date: Fri, 6 Mar 2020 23:46:43 +0100 Subject: Implement openpixelcontrol broadcast (strip 0) input --- backends/openpixelcontrol.c | 191 +++++++++++++++++++++++++++++-------------- backends/openpixelcontrol.md | 3 + 2 files changed, 131 insertions(+), 63 deletions(-) (limited to 'backends/openpixelcontrol.md') diff --git a/backends/openpixelcontrol.c b/backends/openpixelcontrol.c index 62cdb68..ca395d3 100644 --- a/backends/openpixelcontrol.c +++ b/backends/openpixelcontrol.c @@ -1,4 +1,5 @@ #define BACKEND_NAME "openpixelcontrol" +#define DEBUG #include @@ -115,7 +116,7 @@ static ssize_t openpixel_buffer_find(openpixel_instance_data* data, uint8_t stri static int openpixel_buffer_extend(openpixel_instance_data* data, uint8_t strip, uint8_t input, uint16_t length){ ssize_t buffer = openpixel_buffer_find(data, strip, input); - + //length is in component-channels, round it to the nearest rgb-triplet //this guarantees that any allocated buffer has at least three bytes, which is important to parts of the receive handler length = (length % 3) ? ((length / 3) + 1) * 3 : length; @@ -340,12 +341,86 @@ static int openpixel_client_new(instance* inst, int fd){ return mm_manage_fd(fd, BACKEND_NAME, 1, inst); } -static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ - openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; +static size_t openpixel_strip_pixeldata8(instance* inst, openpixel_client* client, uint8_t* data, openpixel_buffer* buffer, size_t bytes_left){ + channel* chan = NULL; + channel_value val; size_t u; + + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= buffer->bytes){ + client->buffer = -2; + break; + } + + //FIXME if at start of trailing non-multiple of 3, ignore + + //update changed channels + if(buffer->data.u8[u + client->offset] != data[u]){ + buffer->data.u8[u + client->offset] = data[u]; + chan = mm_channel(inst, ((uint64_t) buffer->strip << 32) | (u + client->offset + 1), 0); + if(chan){ + //push event + val.raw.u64 = data[u]; + val.normalised = (double) data[u] / 255.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + } + } + return u; +} + +static size_t openpixel_strip_pixeldata16(instance* inst, openpixel_client* client, uint8_t* data, openpixel_buffer* buffer, size_t bytes_left){ channel* chan = NULL; channel_value val; + size_t u; + for(u = 0; u < bytes_left; u++){ + //if over buffer length, ignore + if(u + client->offset >= buffer->bytes){ + client->buffer = -2; + break; + } + + //if at start of trailing non-multiple of 6, ignore + if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ + client->buffer = -2; + break; + } + + //byte-order conversion may be on message boundary, do it via a buffer + client->boundary.u8[(client->offset + u) % 2] = data[u]; + + //detect and update changed channels + if((client->offset + u) % 2 + && buffer->data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ + buffer->data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); + chan = mm_channel(inst, ((uint64_t) buffer->strip << 32) | ((u + client->offset) / 2 + 1), 0); + if(chan){ + //push event + val.raw.u64 = be16toh(client->boundary.u16);; + val.normalised = (double) val.raw.u64 / 65535.0; + if(mm_channel_event(chan, val)){ + LOG("Failed to push channel event to core"); + } + } + + } + } + return u; +} + +static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* client, uint8_t* buffer, size_t bytes_left){ + openpixel_instance_data* data = (openpixel_instance_data*) inst->impl; + openpixel_client temp_client = { + .fd = -1 + }; + ssize_t u, p; + uint8_t processing_done = 1; + + //ignore data if(client->buffer == -2){ //ignore data u = min(client->left, bytes_left); @@ -353,71 +428,55 @@ static ssize_t openpixel_client_pixeldata(instance* inst, openpixel_client* clie client->left -= u; return u; } - else{ - if(data->mode == rgb8){ - for(u = 0; u < bytes_left; u++){ - //if over buffer length, ignore - if(u + client->offset >= data->buffer[client->buffer].bytes){ - client->buffer = -2; - break; + //handle broadcast data + else if(client->buffer == -3){ + //iterate all input strips + for(p = 0; p < data->buffers; p++){ + if(data->buffer[p].flags & OPENPIXEL_INPUT){ + //prepare temporary client + temp_client.buffer = p; + temp_client.hdr = client->hdr; + temp_client.hdr.strip = data->buffer[p].strip; + temp_client.offset = client->offset; + temp_client.left = client->left; + + //run processing on strip + if(data->mode == rgb8){ + openpixel_strip_pixeldata8(inst, &temp_client, buffer, data->buffer + p, bytes_left); } - - //FIXME if at start of trailing non-multiple of 3, ignore - - //update changed channels - if(data->buffer[client->buffer].data.u8[u + client->offset] != buffer[u]){ - data->buffer[client->buffer].data.u8[u + client->offset] = buffer[u]; - chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | (u + client->offset + 1), 0); - if(chan){ - //push event - val.raw.u64 = buffer[u]; - val.normalised = (double) buffer[u] / 255.0; - if(mm_channel_event(chan, val)){ - LOG("Failed to push channel event to core"); - } - } + else{ + openpixel_strip_pixeldata16(inst, &temp_client, buffer, data->buffer + p, bytes_left); + } + if(temp_client.buffer != -2){ + processing_done = 0; } } - - //update offsets - client->offset += u; - client->left -= u; - return u; } - else{ - for(u = 0; u < bytes_left; u++){ - //if over buffer length, ignore - if(u + client->offset >= data->buffer[client->buffer].bytes){ - client->buffer = -2; - break; - } - - //if at start of trailing non-multiple of 6, ignore - if((client->offset + u) >= (client->offset + client->left) - ((client->offset + client->left) % 6)){ - client->buffer = -2; - break; - } - //byte-order conversion may be on message boundary, do it via a buffer - client->boundary.u8[(client->offset + u) % 2] = buffer[u]; - - //detect and update changed channels - if((client->offset + u) % 2 - && data->buffer[client->buffer].data.u16[(u + client->offset) / 2] != be16toh(client->boundary.u16)){ - data->buffer[client->buffer].data.u16[(u + client->offset) / 2] = be16toh(client->boundary.u16); - chan = mm_channel(inst, ((uint64_t) client->hdr.strip << 32) | ((u + client->offset) / 2 + 1), 0); - if(chan){ - //push event - val.raw.u64 = be16toh(client->boundary.u16);; - val.normalised = (double) val.raw.u64 / 65535.0; - if(mm_channel_event(chan, val)){ - LOG("Failed to push channel event to core"); - } - } + //if all strips report being done, ignore the rest of the data + if(processing_done){ + client->buffer = -2; + } - } - } + //remove data + u = min(client->left, bytes_left); + client->offset += u; + client->left -= u; + return u; + } + //process data + else{ + if(data->mode == rgb8){ + u = openpixel_strip_pixeldata8(inst, client, buffer, data->buffer + client->buffer, bytes_left); + } + else{ + u = openpixel_strip_pixeldata16(inst, client, buffer, data->buffer + client->buffer, bytes_left); } + + //update offsets + client->offset += u; + client->left -= u; + return u; } return -1; } @@ -431,8 +490,14 @@ static ssize_t openpixel_client_headerdata(instance* inst, openpixel_client* cli //if done, resolve buffer if(sizeof(openpixel_header) - client->offset <= bytes_left){ - client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); - //TODO handle broadcast strip input + //if broadcast strip, mark broadcast + if(client->hdr.strip == 0 + && data->mode == client->hdr.mode){ + client->buffer = -3; + } + else{ + client->buffer = openpixel_buffer_find(data, client->hdr.strip, 1); + } //if no buffer or mode mismatch, ignore data if(client->buffer < 0 || data->mode != client->hdr.mode){ diff --git a/backends/openpixelcontrol.md b/backends/openpixelcontrol.md index 5a8686f..d09d412 100644 --- a/backends/openpixelcontrol.md +++ b/backends/openpixelcontrol.md @@ -50,3 +50,6 @@ This behaviour may be changed in future releases. While acting as an OpenPixelControl server, the backend allows multiple clients to connect. This may lead to confusing data output when multiple clients are trying to control the same strip. + +When acting as a 16bit OpenPixelControl server, input on the broadcast strip (strip 0) may cause erratic +value events on a few channels, especially with longer strips and inputs. -- cgit v1.2.3