Browse Source

Added lua scripting capabilities

- basic configuration is now done via a lua script
- all the animation is generated by a lua script (see pulsetunnel.lua
  and vumeter.lua for examples)
- basic calculations (FFT, RMS) are done in C and accessible on demand
  from the lua scripts
lua
Thomas Kolb 7 years ago
parent
commit
d4a2e7ef4c
16 changed files with 481 additions and 120 deletions
  1. +7
    -4
      Makefile
  2. +0
    -7
      config.h
  3. +7
    -0
      config.lua
  4. +73
    -0
      lua_utils.c
  5. +20
    -0
      lua_utils.h
  6. +73
    -0
      lua_wrappers.c
  7. +21
    -0
      lua_wrappers.h
  8. +118
    -102
      main.c
  9. +70
    -0
      pulsetunnel.lua
  10. +1
    -1
      run_alsa.sh
  11. +1
    -1
      run_mpd.sh
  12. +2
    -2
      run_pa.sh
  13. +1
    -1
      run_remote.sh
  14. +85
    -0
      vumeter.lua
  15. +1
    -1
      ws2801.c
  16. +1
    -1
      ws2801.h

+ 7
- 4
Makefile View File

@@ -1,10 +1,13 @@
LUA_CFLAGS=$(shell pkg-config --cflags lua)
LUA_LIBS=$(shell pkg-config --libs lua)

CC=gcc
CFLAGS+=-O3 -Wall -march=native -pedantic -std=c99 -D_POSIX_C_SOURCE=20120607L -D_XOPEN_SOURCE
LIBS=-lm -lpthread -lrt
CFLAGS+=-O2 -Wall -march=native -pedantic -std=c99 -D_POSIX_C_SOURCE=20120607L -D_XOPEN_SOURCE $(LUA_CFLAGS)
LIBS=-lm -lpthread -lrt $(LUA_LIBS)

TARGET=musiclight2
SOURCE=main.c fft.c utils.c ws2801.c
DEPS=config.h fft.h utils.h ws2801.h
SOURCE=main.c fft.c utils.c ws2801.c lua_utils.c lua_wrappers.c
DEPS=config.h fft.h utils.h ws2801.h lua_utils.h lua_wrappers.h

OBJ=$(patsubst %.c, %.o, $(SOURCE))


+ 0
- 7
config.h View File

@@ -30,9 +30,6 @@
// update rate for the led strip (in seconds)
#define LED_INTERVAL 0.03

// number of modules in LED strip
#define NUM_MODULES 20

// frequency ranges for the base colors
#define RED_MIN_FREQ 0
#define RED_MAX_FREQ 400
@@ -43,10 +40,6 @@

#define COLOR_MAX_REDUCTION_FACTOR 0.9998

#define CENTER_MODULE 10

#define GAMMA 2.0

// sample data types
typedef int16_t sample;
typedef int64_t sample_sum;

+ 7
- 0
config.lua View File

@@ -0,0 +1,7 @@
WS2801_HOST = "192.168.23.222"
WS2801_PORT = 2703

NUM_MODULES = 20
CENTER_MODULE = 10

GAMMA = 2.0

+ 73
- 0
lua_utils.c View File

@@ -0,0 +1,73 @@
/*
* vim: sw=2 ts=2 expandtab
*
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
* <cfr34k@tkolb.de> wrote this file. As long as you retain this notice you can
* do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a pizza in return. - Thomas Kolb
*/

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#include "lua_utils.h"

void lua_showerror(lua_State *L, const char *msg) {
fprintf(stderr, "\nLUA ERROR:\n %s: %s\n\n",
msg, lua_tostring(L, -1));
}

void lua_pushdoublearray(lua_State *L, double *numbers, size_t len) {
size_t i;

// create an empty table
lua_createtable(L, len, 0);

for(i = 0; i < len; i++) {
// push key and value
lua_pushnumber(L, i+1); // lua arrays count from 1
lua_pushnumber(L, numbers[i]);

// store the values in the table
lua_settable(L, -3);
}
}

void lua_pushsamplearray(lua_State *L, sample *numbers, size_t len) {
size_t i;

// create an empty table
lua_createtable(L, len, 0);

for(i = 0; i < len; i++) {
// push key and value
lua_pushnumber(L, i+1); // lua arrays count from 1
lua_pushnumber(L, numbers[i]);

// store the values in the table
lua_settable(L, -3);
}
}

void lua_readdoublearray(lua_State *L, double *numbers, size_t len) {
size_t k;
double v;

// go to the top of the stack
lua_pushnil(L);

while(lua_next(L, -2)) {
v = lua_tonumber(L, -1);
lua_pop(L, 1);
k = lua_tointeger(L, -1);

if(k > len || k < 1) {
fprintf(stderr, "Warning: Lua index (%u) is out of C array range (%u)!\n", k, len);
} else {
numbers[k-1] = v;
}
}

lua_pop(L, 1);
}

+ 20
- 0
lua_utils.h View File

@@ -0,0 +1,20 @@
/*
* vim: sw=2 ts=2 expandtab
*
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
* <cfr34k@tkolb.de> wrote this file. As long as you retain this notice you can
* do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a pizza in return. - Thomas Kolb
*/

#ifndef LUA_UTILS_H
#define LUA_UTILS_H

#include "config.h"

void lua_showerror(lua_State *L, const char *msg);
void lua_pushdoublearray(lua_State *L, double *numbers, size_t len);
void lua_pushsamplearray(lua_State *L, sample *numbers, size_t len);
void lua_readdoublearray(lua_State *L, double *numbers, size_t len);

#endif // LUA_UTILS_H

+ 73
- 0
lua_wrappers.c View File

@@ -0,0 +1,73 @@
/*
* vim: sw=2 ts=2 expandtab
*
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
* <cfr34k@tkolb.de> wrote this file. As long as you retain this notice you can
* do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a pizza in return. - Thomas Kolb
*/

#include <semaphore.h>

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#include "fft.h"

#include "config.h"

#include "lua_utils.h"
#include "lua_wrappers.h"

extern sem_t fftSemaphore;
extern double fft[BLOCK_LEN];
extern sample signal[BLOCK_LEN];
extern double rms;

static int l_get_energy_in_band(lua_State *L) {
int lowerFreq, higherFreq;

luaL_checktype(L, 1, LUA_TNUMBER);
lowerFreq = lua_tointeger(L, 1);

luaL_checktype(L, 2, LUA_TNUMBER);
higherFreq = lua_tointeger(L, 2);

sem_wait(&fftSemaphore);
lua_pushnumber(L, get_energy_in_band(fft, lowerFreq, higherFreq));
sem_post(&fftSemaphore);

return 1; // number of arguments
}

static int l_get_fft(lua_State *L) {
sem_wait(&fftSemaphore);
lua_pushdoublearray(L, fft, BLOCK_LEN);
sem_post(&fftSemaphore);

return 1; // number of return values
}

static int l_get_signal(lua_State *L) {
sem_wait(&fftSemaphore);
lua_pushsamplearray(L, signal, BLOCK_LEN);
sem_post(&fftSemaphore);

return 1; // number of return values
}

static int l_get_rms(lua_State *L) {
sem_wait(&fftSemaphore);
lua_pushnumber(L, rms);
sem_post(&fftSemaphore);

return 1; // number of return values
}

void lua_register_funcs(lua_State *L) {
lua_register(L, "get_energy_in_band", l_get_energy_in_band);
lua_register(L, "get_fft", l_get_fft);
lua_register(L, "get_signal", l_get_signal);
lua_register(L, "get_rms", l_get_rms);
}

+ 21
- 0
lua_wrappers.h View File

@@ -0,0 +1,21 @@
/*
* vim: sw=2 ts=2 expandtab
*
* THE PIZZA-WARE LICENSE" (derived from "THE BEER-WARE LICENCE"):
* <cfr34k@tkolb.de> wrote this file. As long as you retain this notice you can
* do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a pizza in return. - Thomas Kolb
*/

#ifndef LUA_WRAPPERS_H
#define LUA_WRAPPERS_H

void lua_register_funcs(lua_State *L);

/*
static int l_get_energy_in_band(lua_State *L);
static int l_get_fft(lua_State *L);
static int l_get_signal(lua_State *L);
*/

#endif // LUA_WRAPPERS_H

+ 118
- 102
main.c View File

@@ -14,6 +14,14 @@
#include <math.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#include "lua_utils.h"
#include "lua_wrappers.h"

#include "fft.h"
#include "utils.h"
@@ -27,12 +35,9 @@
// Number of new samples put into the buffer each frame
#define READ_SAMPLES (BLOCK_LEN / BUFFER_PARTS)

#define COLORBUF_SIZE (2*(NUM_MODULES+1))
#define CENTER_POS (2*CENTER_MODULE)

double fft[BLOCK_LEN];
sample signal[BLOCK_LEN];
double rms;
double redEnergy, greenEnergy, blueEnergy;
double lastUpdateTime = 0;

sem_t fftSemaphore;
@@ -72,7 +77,7 @@ void* fft_thread(void *param) {

tmpRMS = 0;
for(i = 0; i < BLOCK_LEN; i++) {
tmpRMS += block[i]*block[i];
tmpRMS += buffer[i]*buffer[i];
}
tmpRMS = sqrt(tmpRMS/BLOCK_LEN);

@@ -80,10 +85,8 @@ void* fft_thread(void *param) {
sem_wait(&fftSemaphore);

memcpy(fft, tmpFFT, sizeof(fft));
memcpy(signal, buffer, sizeof(signal));
rms = tmpRMS;
redEnergy = get_energy_in_band(fft, RED_MIN_FREQ, RED_MAX_FREQ);
greenEnergy = get_energy_in_band(fft, GREEN_MIN_FREQ, GREEN_MAX_FREQ);
blueEnergy = get_energy_in_band(fft, BLUE_MIN_FREQ, BLUE_MAX_FREQ);

curTime = get_hires_time();
lastUpdateTime = curTime;
@@ -113,64 +116,97 @@ void* fft_thread(void *param) {
return NULL;
}

double gamma_correct(double d) {
return pow(d, GAMMA);
double gamma_correct(double d, double gamma) {
return pow(d, gamma);
}

void text_bar(double fill) {
int fillCnt = 10 * fill;
int i;
int main(int argc, char **argv) {
double nextFrame = get_hires_time() + LED_INTERVAL;

for(i = 0; i < fillCnt; i++) {
printf("|");
}
int i;
pthread_t fftThread;

int active = 1;

double *red;
double *green;
double *blue;

int useFading, fadeStep;

for(; i < 10; i++) {
printf("-");
if(argc < 2) {
fprintf(stderr, "LUA script file must be given as command line argument!\n");
return 1;
}
}

double weighted_avg(uint8_t colorBuf[COLORBUF_SIZE][3], int channel, int centerPos) {
return 0.20 * colorBuf[centerPos - 1][channel] +
0.60 * colorBuf[centerPos][channel] +
0.20 * colorBuf[centerPos + 1][channel];
}
// initialize lua
lua_State *L = lua_open();

void show_status(double curRed, double maxRed, double curGreen, double maxGreen, double curBlue, double maxBlue) {
printf("\r");
// load the lua libraries
luaL_openlibs(L);

printf("[\033[31m");
text_bar(curRed / maxRed);
printf("\033[0m] ");
printf("\033[1;31m%7.2e\033[0m ", maxRed / (RED_MAX_FREQ - RED_MIN_FREQ));
// register local functions
lua_register_funcs(L);

printf("[\033[32m");
text_bar(curGreen / maxGreen);
printf("\033[0m] ");
printf("\033[1;32m%7.2e\033[0m ", maxGreen / (GREEN_MAX_FREQ - GREEN_MIN_FREQ));
// load the configuration from "config.lua"
if(luaL_dofile(L, "config.lua")) {
lua_showerror(L, "luaL_dofile(config.lua) failed.");
return 1;
}

printf("[\033[34m");
text_bar(curBlue / maxBlue);
printf("\033[0m] ");
printf("\033[1;34m%7.2e\033[0m ", maxBlue / (BLUE_MAX_FREQ - BLUE_MIN_FREQ));
lua_getglobal(L, "WS2801_HOST");
if(!lua_isstring(L, -1)) return 2;
const char *host = lua_tostring(L, -1);

fflush(stdout);
}
lua_getglobal(L, "WS2801_PORT");
if(!lua_isnumber(L, -1)) return 2;
unsigned short port = lua_tointeger(L, -1);

int main(int argc, char **argv) {
double nextFrame = get_hires_time() + LED_INTERVAL;
lua_getglobal(L, "GAMMA");
if(!lua_isnumber(L, -1)) return 2;
double gamma = lua_tonumber(L, -1);

int i, j;
pthread_t fftThread;
lua_getglobal(L, "NUM_MODULES");
if(!lua_isnumber(L, -1)) return 2;
int num_modules = lua_tointeger(L, -1);

int active = 1;
lua_getglobal(L, "CENTER_MODULE");
if(!lua_isnumber(L, -1)) return 2;
int center_module = lua_tointeger(L, -1);

// allocate arrays
red = malloc(num_modules * sizeof(double));
green = malloc(num_modules * sizeof(double));
blue = malloc(num_modules * sizeof(double));

uint8_t colorBuf[COLORBUF_SIZE][3];
// load and initialize the script
if(luaL_loadfile(L, argv[1])) {
lua_showerror(L, "luaL_loadfile(cmdline_argument) failed.");
}

double curRedEnergy, curGreenEnergy, curBlueEnergy;
double maxRedEnergy = 1, maxGreenEnergy = 1, maxBlueEnergy = 1;
// priming call: read the lua file to make functions known
if(lua_pcall(L, 0, 0, 0)) {
lua_showerror(L, "lua_pcall failed.");
}

memset(colorBuf, 0, sizeof(colorBuf));
// call the init function
lua_getglobal(L, "init");
lua_pushnumber(L, num_modules);
lua_pushnumber(L, center_module);
if(lua_pcall(L, 2, 1, 0)) {
lua_showerror(L, "lua_pcall(init) failed.");
}

fadeStep = lua_tointeger(L, -1);
useFading = fadeStep > 0;
if(useFading) {
ws2801_set_fadestep(fadeStep);
printf("Fading enabled with fadestep %i.\n", fadeStep);
}

// initialize the WS2801 library
printf("Connecting to %s:%i\n", host, port);
ws2801_init(host, port);

// create semaphores
sem_init(&fftSemaphore, 0, 1);
@@ -178,68 +214,41 @@ int main(int argc, char **argv) {
// run the fft thread
pthread_create(&fftThread, NULL, fft_thread, NULL);

ws2801_init(HOST, PORT);

while(running) {
for(i = COLORBUF_SIZE-1; i >= 0; i--) {
int pos = CENTER_POS + i;
if(pos < COLORBUF_SIZE-1) {
for(j = 0; j < 3; j++) {
colorBuf[pos][j] = colorBuf[pos - 1][j];
}
}

pos = CENTER_POS - i;
if(pos >= 0) {
for(j = 0; j < 3; j++) {
colorBuf[pos][j] = colorBuf[pos + 1][j];
}
}
}

if(active) {
sem_wait(&fftSemaphore);
curRedEnergy = redEnergy;
curGreenEnergy = greenEnergy;
curBlueEnergy = blueEnergy;
sem_post(&fftSemaphore);

maxRedEnergy *= COLOR_MAX_REDUCTION_FACTOR;
if(curRedEnergy > maxRedEnergy) {
maxRedEnergy = curRedEnergy;
// call the periodic() function from LUA
lua_getglobal(L, "periodic");
if(lua_pcall(L, 0, 3, 0)) { // no arguments, 3 return values
lua_showerror(L, "lua_pcall(periodic) failed.");
}

maxGreenEnergy *= COLOR_MAX_REDUCTION_FACTOR;
if(curGreenEnergy > maxGreenEnergy) {
maxGreenEnergy = curGreenEnergy;
}

maxBlueEnergy *= COLOR_MAX_REDUCTION_FACTOR;
if(curBlueEnergy > maxBlueEnergy) {
maxBlueEnergy = curBlueEnergy;
}

colorBuf[CENTER_POS][0] = 255 * gamma_correct(curRedEnergy / maxRedEnergy);
colorBuf[CENTER_POS][1] = 255 * gamma_correct(curGreenEnergy / maxGreenEnergy);
colorBuf[CENTER_POS][2] = 255 * gamma_correct(curBlueEnergy / maxBlueEnergy);

/*
show_status(curRedEnergy, maxRedEnergy, curGreenEnergy, maxGreenEnergy,
curBlueEnergy, maxBlueEnergy);
*/

for(i = 0; i < NUM_MODULES; i++) {
ws2801_set_color(i,
weighted_avg(colorBuf, 0, 2 * i + 1),
weighted_avg(colorBuf, 1, 2 * i + 1),
weighted_avg(colorBuf, 2, 2 * i + 1));
// read the return values (reverse order, as lua uses a stack)
lua_readdoublearray(L, blue, num_modules);
lua_readdoublearray(L, green, num_modules);
lua_readdoublearray(L, red, num_modules);

if(useFading) {
for(i = 0; i < num_modules; i++) {
ws2801_fade_color(i,
255 * gamma_correct(red[i], gamma),
255 * gamma_correct(green[i], gamma),
255 * gamma_correct(blue[i], gamma));
}
ws2801_commit();
} else {
for(i = 0; i < num_modules; i++) {
ws2801_set_color(i,
255 * gamma_correct(red[i], gamma),
255 * gamma_correct(green[i], gamma),
255 * gamma_correct(blue[i], gamma));
}
ws2801_commit();
}
ws2801_commit();

if(lastUpdateTime < nextFrame - 1) {
printf("Idle for 1 second -> stopping updates.\n");

for(i = 0; i < NUM_MODULES; i++) {
for(i = 0; i < num_modules; i++) {
ws2801_fade_color(i, 20, 20, 20);
}
ws2801_commit();
@@ -257,7 +266,14 @@ int main(int argc, char **argv) {

ws2801_shutdown();

// free arrays
free(red);
free(green);
free(blue);

pthread_join(fftThread, NULL);

lua_close(L);

return 0;
}

+ 70
- 0
pulsetunnel.lua View File

@@ -0,0 +1,70 @@
COOLDOWN_FACTOR = 0.9998

num_modules = 20
center_module = 10

-- maximum energy values for each band
maxRedEnergy = 1
maxGreenEnergy = 1
maxBlueEnergy = 1

-- output color buffers
red = {}
green = {}
blue = {}

function periodic()
local redEnergy = get_energy_in_band(0, 400);
local greenEnergy = get_energy_in_band(400, 4000);
local blueEnergy = get_energy_in_band(4000, 22000);

maxRedEnergy = maxRedEnergy * COOLDOWN_FACTOR
if redEnergy > maxRedEnergy then
maxRedEnergy = redEnergy
end

maxGreenEnergy = maxGreenEnergy * COOLDOWN_FACTOR
if greenEnergy > maxGreenEnergy then
maxGreenEnergy = greenEnergy
end

maxBlueEnergy = maxBlueEnergy * COOLDOWN_FACTOR
if blueEnergy > maxBlueEnergy then
maxBlueEnergy = blueEnergy
end

-- move the color buffers on by one in each direction
for i = 2,center_module,1 do
red[i-1] = red[i]
green[i-1] = green[i]
blue[i-1] = blue[i]
end

for i = num_modules-1,center_module,-1 do
red[i+1] = red[i]
green[i+1] = green[i]
blue[i+1] = blue[i]
end

-- set the new value for the center module
red[center_module] = redEnergy / maxRedEnergy
green[center_module] = greenEnergy / maxGreenEnergy
blue[center_module] = blueEnergy / maxBlueEnergy

-- return the 3 color arrays
return red, green, blue
end

function init(nmod, cmod)
num_modules = nmod
center_module = cmod

for i = 1,nmod do
red[i] = 0
green[i] = 0
blue[i] = 0
end

-- don't use fading
return 0
end

+ 1
- 1
run_alsa.sh View File

@@ -1,3 +1,3 @@
#!/bin/sh

arecord -c 1 -f s16 -r 22050 | ./rtfft
arecord -c 1 -f s16 -r 22050 | ./musiclight2 $*

+ 1
- 1
run_mpd.sh View File

@@ -1,4 +1,4 @@
#!/bin/sh

#dd if=/tmp/mpd.fifo bs=1024 | ./musiclight2
./musiclight2 < /tmp/mpd.fifo
./musiclight2 $* < /tmp/mpd.fifo

+ 2
- 2
run_pa.sh View File

@@ -5,12 +5,12 @@
case $1 in
mic)
#mikro
parec -d "alsa_input.pci-0000_00_1b.0.analog-stereo" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2
parec -d "alsa_input.pci-0000_00_1b.0.analog-stereo" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2 $*
;;

*)
# soundkarte
parec -d "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2
parec -d "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor" --raw --rate=44100 --channels=1 --format=s16 | ./musiclight2 $*
;;
esac


+ 1
- 1
run_remote.sh View File

@@ -1,3 +1,3 @@
#!/bin/sh

nc -l -p 12345 | ./musiclight2
nc -l -p 12345 | ./musiclight2 $*

+ 85
- 0
vumeter.lua View File

@@ -0,0 +1,85 @@
COOLDOWN_FACTOR = 0.9998
FACTOR = 0.2

num_modules = 20
center_module = 10

-- maximum energy values for each band
maxRedEnergy = 1
maxGreenEnergy = 1
maxBlueEnergy = 1
maxRMS = 1

-- output color buffers
red = {}
green = {}
blue = {}

function periodic()
local redEnergy = get_energy_in_band(0, 400);
local greenEnergy = get_energy_in_band(400, 4000);
local blueEnergy = get_energy_in_band(4000, 22000);
local rms = get_rms();

maxRedEnergy = maxRedEnergy * COOLDOWN_FACTOR
if redEnergy > maxRedEnergy then
maxRedEnergy = redEnergy
end

maxGreenEnergy = maxGreenEnergy * COOLDOWN_FACTOR
if greenEnergy > maxGreenEnergy then
maxGreenEnergy = greenEnergy
end

maxBlueEnergy = maxBlueEnergy * COOLDOWN_FACTOR
if blueEnergy > maxBlueEnergy then
maxBlueEnergy = blueEnergy
end

maxRMS = maxRMS * COOLDOWN_FACTOR
if rms > maxRMS then
maxRMS = rms
end

local brightness = rms / maxRMS

for i = 1,num_modules do
if i <= num_modules * redEnergy / maxRedEnergy then
redTarget = brightness
else
redTarget = 0
end
red[i] = (1 - FACTOR) * red[i] + FACTOR * redTarget;

if i <= num_modules * greenEnergy / maxGreenEnergy then
greenTarget = brightness
else
greenTarget = 0
end
green[i] = (1 - FACTOR) * green[i] + FACTOR * greenTarget;

if i <= num_modules * blueEnergy / maxBlueEnergy then
blueTarget = brightness
else
blueTarget = 0
end
blue[i] = (1 - FACTOR) * blue[i] + FACTOR * blueTarget;
end

-- return the 3 color arrays
return red, green, blue
end

function init(nmod, cmod)
num_modules = nmod
center_module = cmod

for i = 1,nmod do
red[i] = 0
green[i] = 0
blue[i] = 0
end

-- fadestep (0 = no fading)
return 0
end

+ 1
- 1
ws2801.c View File

@@ -32,7 +32,7 @@ struct WS2801Packet packetQueue[50];
int queueIndex = 0;

// creates the socket needed for steering the LED strip
int ws2801_init(char *host, unsigned short port) {
int ws2801_init(const char *host, unsigned short port) {
struct addrinfo hints;
struct addrinfo *result;
char portstr[6];

+ 1
- 1
ws2801.h View File

@@ -10,7 +10,7 @@
#ifndef WS2801_H
#define WS2801_H

int ws2801_init(char *host, unsigned short port);
int ws2801_init(const char *host, unsigned short port);
void ws2801_set_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);
void ws2801_fade_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);
void ws2801_add_color(uint8_t module, uint8_t r, uint8_t g, uint8_t b);

Loading…
Cancel
Save