182 lines
5.2 KiB
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;
|
|
}
|