Adalight-FastLED_rgbwMod/C/colorswirl.c

182 lines
5.2 KiB
C

/*
"Colorswirl" LED demo. This is the host PC-side code written in C;
intended for use with a USB-connected Arduino microcontroller running the
accompanying LED streaming code. Requires one strand of Digital RGB LED
Pixels (Adafruit product ID #322, specifically the newer WS2801-based type,
strand of 25) and a 5 Volt power supply (such as Adafruit #276). You may
need to adapt the code and the hardware arrangement for your specific
configuration.
This is a command-line program. It expects a single parameter, which is
the serial port device name, e.g.:
./colorswirl /dev/tty.usbserial-A60049KO
*/
// --------------------------------------------------------------------
// This file is part of Adalight.
// Adalight is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
// Adalight is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with Adalight. If not, see
// <http://www.gnu.org/licenses/>.
// --------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <time.h>
#include <math.h>
#define N_LEDS 25 // Max of 65536
int main(int argc,char *argv[])
{
int fd, i, bytesToGo, bytesSent, totalBytesSent = 0,
frame = 0, hue1, hue2, brightness;
unsigned char buffer[6 + (N_LEDS * 3)], // Header + 3 bytes per LED
lo, r, g, b;
double sine1, sine2;
time_t t, start, prev;
struct termios tty;
if(argc < 2) {
(void)printf("Usage: %s device\n", argv[0]);
return 1;
}
if((fd = open(argv[1],O_RDWR | O_NOCTTY | O_NONBLOCK)) < 0) {
(void)printf("Can't open device '%s'.\n", argv[1]);
return 1;
}
// Serial port config swiped from RXTX library (rxtx.qbang.org):
tcgetattr(fd, &tty);
tty.c_iflag = INPCK;
tty.c_lflag = 0;
tty.c_oflag = 0;
tty.c_cflag = CREAD | CS8 | CLOCAL;
tty.c_cc[ VMIN ] = 0;
tty.c_cc[ VTIME ] = 0;
cfsetispeed(&tty, B115200);
cfsetospeed(&tty, B115200);
tcsetattr(fd, TCSANOW, &tty);
bzero(buffer, sizeof(buffer)); // Clear LED buffer
// Header only needs to be initialized once, not
// inside rendering loop -- number of LEDs is constant:
buffer[0] = 'A'; // Magic word
buffer[1] = 'd';
buffer[2] = 'a';
buffer[3] = (N_LEDS - 1) >> 8; // LED count high byte
buffer[4] = (N_LEDS - 1) & 0xff; // LED count low byte
buffer[5] = buffer[3] ^ buffer[4] ^ 0x55; // Checksum
sine1 = 0.0;
hue1 = 0;
prev = start = time(NULL); // For bandwidth statistics
for(;;) {
sine2 = sine1;
hue2 = hue1;
// Start at position 6, after the LED header/magic word
for(i = 6; i < sizeof(buffer); ) {
// Fixed-point hue-to-RGB conversion. 'hue2' is an
// integer in the range of 0 to 1535, where 0 = red,
// 256 = yellow, 512 = green, etc. The high byte
// (0-5) corresponds to the sextant within the color
// wheel, while the low byte (0-255) is the
// fractional part between primary/secondary colors.
lo = hue2 & 255;
switch((hue2 >> 8) % 6) {
case 0:
r = 255;
g = lo;
b = 0;
break;
case 1:
r = 255 - lo;
g = 255;
b = 0;
break;
case 2:
r = 0;
g = 255;
b = lo;
break;
case 3:
r = 0;
g = 255 - lo;
b = 255;
break;
case 4:
r = lo;
g = 0;
b = 255;
break;
case 5:
r = 255;
g = 0;
b = 255 - lo;
break;
}
// Resulting hue is multiplied by brightness in the
// range of 0 to 255 (0 = off, 255 = brightest).
// Gamma corrrection (the 'pow' function here) adjusts
// the brightness to be more perceptually linear.
brightness = (int)(pow(0.5+sin(sine2)*0.5,3.0)*255.0);
buffer[i++] = (r * brightness) / 255;
buffer[i++] = (g * brightness) / 255;
buffer[i++] = (b * brightness) / 255;
// Each pixel is offset in both hue and brightness
hue2 += 40;
sine2 += 0.3;
}
// Slowly rotate hue and brightness in opposite directions
hue1 = (hue1 + 5) % 1536;
sine1 -= .03;
// Issue color data to LEDs. Each OS is fussy in different
// ways about serial output. This arrangement of drain-and-
// write-loop seems to be the most relable across platforms:
tcdrain(fd);
for(bytesSent=0, bytesToGo=sizeof(buffer); bytesToGo > 0;) {
if((i=write(fd,&buffer[bytesSent],bytesToGo)) > 0) {
bytesToGo -= i;
bytesSent += i;
}
}
// Keep track of byte and frame counts for statistics
totalBytesSent += sizeof(buffer);
frame++;
// Update statistics once per second
if((t = time(NULL)) != prev) {
(void)printf(
"Average frames/sec: %d, bytes/sec: %d\n",
(int)((float)frame / (float)(t - start)),
(int)((float)totalBytesSent / (float)(t - start)));
prev = t;
}
}
close(fd);
return 0;
}