This commit is contained in:
Steffen Vogel 2011-01-26 17:58:25 +01:00
parent 09c4686f79
commit 2ea30ea3d3
16 changed files with 722 additions and 237 deletions

6
README
View file

@ -4,7 +4,11 @@ This is a simple c lib for controlling your fnordlichts.
It's written by Steffen Vogel <info@steffenvogel.de> and licenced under GPLv3 or newer.
Please contact me by mail for bug reports or annotations.
The source is including a small cli program and a vu meter to show your audio level on the lights.
To connect the fnordlichts to your computer you need a serial port or an USB to RS232 adapter and a RS232 to TTL level shifter (MAX232).
Connect your CTS line to the interrupt line of the fnordlicht bus to be able to count the devices on the bus.
fnctl & fnvum
-------------------------------------------------------------------------------
The source is including a small cli program and a vu meter to show your audio level on the lights.

View file

@ -26,10 +26,6 @@
#ifndef COLOR_H
#define COLOR_H
#if defined(PWM_CHANNELS) && PWM_CHANNELS != 3
#error "PWM_CHANNELS is not 3, this is unsupported!"
#endif
struct rgb_color_t
{
union {

BIN
fnctl

Binary file not shown.

327
fnctl.c
View file

@ -10,8 +10,26 @@
#include "libfn.h"
typedef struct { char * name; char * description; uint8_t cmd; } command_t;
static command_t commands[] = {
/* local commands (>= 0xA0) */
#define LOCAL_CMD_EEPROM 0xA0
#define LOCAL_CMD_COUNT 0xA1
#define DEFAULT_HOST "localhost"
#define DEFAULT_PORT "7970"
#define DEFAULT_DEVICE "/dev/ttyUSB0"
struct command_t {
char * name;
char * description;
uint8_t cmd;
};
enum connection_t {
RS232,
NET
};
static struct command_t commands[] = {
{"fade", "set color/fade to color", REMOTE_CMD_FADE_RGB},
{"save", "save color to EEPROM", REMOTE_CMD_SAVE_RGB},
{"modify", "modify current color", REMOTE_CMD_MODIFY_CURRENT},
@ -20,7 +38,9 @@ static command_t commands[] = {
{"powerdown", "power down the device", REMOTE_CMD_POWERDOWN},
{"config", "configure startup & offsets", REMOTE_CMD_CONFIG_OFFSETS},
{"reset", "reset fnordlichter", REMOTE_CMD_BOOTLOADER},
{0, 0} /* stop condition for iterator */
{"eeprom", "put sequence to EEPROM", LOCAL_CMD_EEPROM},
{"count", "count modules on the bus", LOCAL_CMD_COUNT},
{0} /* stop condition for iterator */
};
static struct option long_options[] = {
@ -28,16 +48,30 @@ static struct option long_options[] = {
{"step", required_argument, 0, 's'},
{"address", required_argument, 0, 'a'},
{"mask", required_argument, 0, 'm'},
{"slot", required_argument, 0, 't'},
{"slot", required_argument, 0, 'w'},
{"pause", required_argument, 0, 'p'},
{"color", required_argument, 0, 'c'},
{"start", required_argument, 0, 'f'},
{"end", required_argument, 0, 't'},
{"repeat", required_argument, 0, 'r'},
{"help", required_argument, 0, 'h'},
{"port", required_argument, 0, 'P'},
{"host", required_argument, 0, 'H'},
{"filename", required_argument, 0, 'F'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0} /* stop condition for iterator */
{0} /* stop condition for iterator */
};
struct rgb_color_t parse_color(char * identifier) {
struct rgb_color_t color;
if (strlen(identifier) != 6) {
fprintf(stderr, "invalid color definition: %s", identifier);
}
sscanf(identifier, "%2x%2x%2x", (unsigned int *) (&color.red), (unsigned int *) (&color.green), (unsigned int *) (&color.blue));
return color;
}
void print_cmd(struct remote_msg_t * msg) {
printf("sending: ");
@ -52,7 +86,7 @@ void usage(char ** argv) {
printf("usage: %s command [options]\n\n", argv[0]);
printf(" following commands are available:\n");
command_t * cp = commands;
struct command_t * cp = commands;
while (cp->name) {
printf("\t%s%s%s\n", cp->name, (strlen(cp->name) < 7) ? "\t\t" : "\t", cp->description);
cp++;
@ -68,17 +102,6 @@ void usage(char ** argv) {
}
}
struct rgb_color_t parse_color(char * color_def) {
struct rgb_color_t color;
if (strlen(color_def) != 6)
fprintf(stderr, "invalid color definition: %s", color_def);
sscanf(color_def, "%2x%2x%2x", (unsigned int *) (&color.red), (unsigned int *) (&color.green), (unsigned int *) (&color.blue));
return color;
}
int main(int argc, char ** argv) {
/* options */
uint8_t address = 255;
@ -86,12 +109,19 @@ int main(int argc, char ** argv) {
uint8_t slot = 0;
uint8_t delay = 0;
uint8_t pause = 0;
char host[255];
char port[255] = "7970";
int verbose;
char mask[254] = "";
char filename[1024] = "";
enum connection_t con_mode = RS232;
char host[255] = DEFAULT_HOST;
char port[255] = DEFAULT_PORT;
char device[255] = DEFAULT_DEVICE;
int verbose = 0;
struct rgb_color_t color;
struct remote_msg_t msg;
union program_params_t params;
memset(&msg, 0, sizeof msg);
/* connection */
@ -106,15 +136,17 @@ int main(int argc, char ** argv) {
exit(-1);
}
command_t * cp = commands;
while (cp->name && strcmp(cp->name, argv[1]) != 0) { cp++; }
struct command_t * cp = commands;
while (cp->name && strcmp(cp->name, argv[1]) != 0) {
cp++;
}
/* parse cli arguments */
while (1) {
/* getopt_long stores the option index here. */
int option_index = 0;
int c = getopt_long(argc, argv, "hva:m:d:s:d:p:c:P:H:", long_options, &option_index);
int c = getopt_long(argc, argv, "hva:m:f:d:t:s:f:w:r:d:p:c:P:H:F:", long_options, &option_index);
/* Detect the end of the options. */
if (c == -1)
@ -126,8 +158,7 @@ int main(int argc, char ** argv) {
break;
case 'm':
fprintf(stderr, "not yet implemented\n");
exit(-1);
strcpy(mask, optarg);
break;
case 's':
@ -138,7 +169,7 @@ int main(int argc, char ** argv) {
delay = atoi(optarg);
break;
case 't':
case 'w':
slot = atoi(optarg);
break;
@ -149,6 +180,30 @@ int main(int argc, char ** argv) {
case 'c':
color = parse_color(optarg);
break;
case 'f':
params.replay.start = atoi(optarg);
break;
case 't':
params.replay.end = atoi(optarg);
break;
case 'r':
if (strcmp("none", optarg) == 0) {
params.replay.repeat = REPEAT_NONE;
}
else if (strcmp("start", optarg) == 0) {
params.replay.repeat = REPEAT_START;
}
else if (strcmp("reverse", optarg) == 0) {
params.replay.repeat = REPEAT_REVERSE;
}
else {
fprintf(stderr, "invalid --repeat value: %s\n", optarg);
exit(-1);
}
break;
case 'H': {
char * ps = strrchr(optarg, ':');
@ -159,34 +214,16 @@ int main(int argc, char ** argv) {
else { /* without port */
strcpy(host, optarg);
}
con_mode = NET;
break;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* both IPv4 & IPv6 */
hints.ai_socktype = SOCK_STREAM;
getaddrinfo(host, port, &hints, &res);
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
connect(fd, res->ai_addr, res->ai_addrlen);
if (fd < 0) {
perror(port);
exit(-1);
}
fn_sync(fd);
usleep(200000);
break;
case 'P':
fd = open(optarg, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror(optarg);
exit(-1);
}
oldtio = fn_init(fd);
strcpy(port, optarg);
break;
case 'F':
strcpy(filename, optarg);
break;
case 'v':
@ -200,91 +237,179 @@ int main(int argc, char ** argv) {
}
}
/* connect to fnordlichter */
if (con_mode == NET) {
if (verbose) printf("connect via net: %s:%s\n", host, port);
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* both IPv4 & IPv6 */
hints.ai_socktype = SOCK_STREAM;
getaddrinfo(host, port, &hints, &res);
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
connect(fd, res->ai_addr, res->ai_addrlen);
if (fd < 0) {
perror(host);
exit(-1);
}
}
else {
if (verbose) printf("connect via rs232: %s\n", device);
fd = open(device, O_RDWR | O_NOCTTY);
if (fd < 0) {
perror(port);
exit(-1);
}
oldtio = fn_init(fd);
}
fn_sync(fd);
/* check address */
if (strlen(host) == 0 && address != 255 && address > fn_count_devices(fd)) {
fprintf(stderr, "device with address %d not found\n", address);
if (address > FN_MAX_DEVICES+1) {
fprintf(stderr, "sorry, the fnordlicht bus can't address more the %d devices\n", FN_MAX_DEVICES);
exit(-1);
}
if (verbose) {
printf("command: %s: %s\n", cp->name, cp->description);
printf("port: %s\n", port);
printf("host: %s\n", host);
printf("address: %d\n", address);
printf("found %d fnordlichts\n", fn_count_devices(fd));
}
if (verbose) printf("command: %s (%s)\n", cp->name, cp->description);
switch (cp->cmd) {
/* remote commands */
case REMOTE_CMD_FADE_RGB: {
struct remote_msg_fade_rgb_t * param = (void *) &msg;
struct remote_msg_fade_rgb_t * cmsg = (void *) &msg;
cmsg->step = step;
cmsg->delay = delay;
cmsg->color = color;
param->step = step;
param->delay = delay;
param->color = color;
}
break;
}
case REMOTE_CMD_MODIFY_CURRENT: {
struct remote_msg_modify_current_t * param = (void *) &msg;
struct remote_msg_modify_current_t * cmsg = (void *) &msg;
struct rgb_color_offset_t ofs = {50,50,50};
struct rgb_color_offset_t ofs = {50, 50, 50};
param->step = step;
param->delay = delay;
param->rgb = ofs;
cmsg->step = step;
cmsg->delay = delay;
cmsg->rgb = ofs;
}
break;
}
case REMOTE_CMD_SAVE_RGB: {
struct remote_msg_save_rgb_t * param = (void *) &msg;
struct remote_msg_save_rgb_t * cmsg = (void *) &msg;
param->slot = slot;
param->step = step;
param->delay = delay;
param->pause = pause;
param->color = color;
}
cmsg->slot = slot;
cmsg->step = step;
cmsg->delay = delay;
cmsg->pause = pause;
cmsg->color = color;
break;
}
case REMOTE_CMD_START_PROGRAM: {
struct remote_msg_start_program_t * param = (void *) &msg;
}
struct remote_msg_start_program_t * cmsg = (void *) &msg;
cmsg->script = 2;
cmsg->params = params;
break;
case REMOTE_CMD_STOP: {
struct remote_msg_stop_t * param = (void *) &msg;
}
break;
/* no special parameters */
case REMOTE_CMD_STOP:
case REMOTE_CMD_POWERDOWN:
case REMOTE_CMD_BOOTLOADER:
break;
/* local commands */
case LOCAL_CMD_COUNT:
printf("%d\n", fn_count_devices(fd));
break;
case LOCAL_CMD_EEPROM: {
FILE *eeprom_file = fopen(filename, "r");
char row[1024];
if (eeprom_file == NULL) {
perror ("error opening eeprom file");
exit(-1);
}
while(!feof(eeprom_file)) {
if (fgets(row, 1024, eeprom_file) && *row != '#') { /* ignore comments */
struct remote_msg_save_rgb_t msg;
memset(&msg, 0, sizeof msg);
msg.cmd = REMOTE_CMD_SAVE_RGB;
sscanf(row, "%d;%d;%2x%2x%2x;%d;%d;%d",
(unsigned int *) &msg.slot,
(unsigned int *) &msg.address,
(unsigned int *) (&color.red),
(unsigned int *) (&color.green),
(unsigned int *) (&color.blue),
(unsigned int *) &msg.step,
(unsigned int *) &msg.delay,
(unsigned int *) &msg.pause
);
int p = fn_send(fd, (struct remote_msg_t *) &msg);
if (verbose) print_cmd((struct remote_msg_t *) &msg);
if (p < 0) {
fprintf(stderr, "failed on writing %d bytes to fnordlichts", REMOTE_MSG_LEN);
exit(-1);
}
}
}
break;
}
default:
fprintf(stderr, "unknown subcomand: %s\n", argv[1]);
usage(argv);
exit(-1);
}
msg.address = address;
msg.cmd = cp->cmd;
/* send remote commands to bus */
if (cp->cmd < 0xA0) {
if (verbose) printf("address: %d\n", address);
msg.address = address;
msg.cmd = cp->cmd;
int i = fn_send(fd, &msg);
if (i < 0)
fprintf(stderr, "failed on writing %d bytes to fnordlichts", REMOTE_MSG_LEN);
if (verbose) {
print_cmd(&msg);
}
if (strlen(host) == 0) {
/* reset port to old state */
tcsetattr(fd, TCSANOW, &oldtio);
int c = strlen(mask);
if (c > 0) {
if (verbose) printf("sending to mask: %s\n", mask);
int i;
for (i = 0; i < c; i++) {
if (mask[i] == '1') {
msg.address = i;
int p = fn_send(fd, &msg);
if (verbose) print_cmd(&msg);
if (p < 0) {
fprintf(stderr, "failed on writing %d bytes to fnordlichts", REMOTE_MSG_LEN);
exit(-1);
}
}
else if (mask[i] != '0') {
fprintf(stderr, "invalid mask! only '0' and '1' are allowed\n");
exit(-1);
}
}
}
else {
int p = fn_send(fd, &msg);
if (verbose) print_cmd(&msg);
if (p < 0) {
fprintf(stderr, "failed on writing %d bytes to fnordlichts", REMOTE_MSG_LEN);
exit(-1);
}
}
}
if (con_mode == RS232) tcsetattr(fd, TCSANOW, &oldtio); /* reset port to old state */
return 0;
}

BIN
fnctl.o

Binary file not shown.

181
fnvum.c Normal file
View file

@ -0,0 +1,181 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include <termios.h>
#include <fcntl.h>
#include <complex.h>
/* third party libs */
#include "libfn.h"
#include <SDL/SDL.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/gccmacro.h>
#include <fftw3.h>
#define BUFFER_SIZE 1024
#define SAMPLING_RATE 44100
#define SCREEN_WIDTH (BUFFER_SIZE/2+1)
#define SCREEN_HEIGHT (SCREEN_WIDTH / 2)
#define TITLE "fnordlicht vumeter & spectrum"
void show_level(SDL_Surface *dst, float level) {
SDL_Rect rect = dst->clip_rect;
Uint32 color = SDL_MapRGB(dst->format, 0xff, 0, 0);
Uint32 background = SDL_MapRGB(dst->format, 0, 0, 0);
SDL_FillRect(dst, &rect, background);
rect.w *= level;
SDL_FillRect(dst, &rect, color);
SDL_Flip(dst);
}
void show_spectrum(SDL_Surface * dst, complex * fft_data) {
int x, p, start, end;
double log_data[BUFFER_SIZE];
SDL_Rect rect;
Uint32 color = SDL_MapRGB(dst->format, 0xff, 0, 0);
Uint32 background = SDL_MapRGB(dst->format, 0, 0, 0);
SDL_FillRect(dst, &dst->clip_rect, background);
double ampl, db;
rect.w = 1;
for (x = 0; x < BUFFER_SIZE/2+1; x++) {
ampl = cabs(fft_data[x]);
rect.x = x;
// rect.h = (ampl/3000000) * SCREEN_HEIGHT;
rect.h = 10 * log10(ampl/300000) * SCREEN_HEIGHT;
rect.y = SCREEN_HEIGHT - rect.h;
SDL_FillRect(dst, &rect, color);
}
SDL_Flip(dst);
}
void fade_level(int fd, float level) {
struct remote_msg_fade_rgb_t fncmd;
memset(&fncmd, 0, sizeof (struct remote_msg_t));
fncmd.color.red = fncmd.color.green = fncmd.color.blue = (uint8_t) 255.0 * level;
fncmd.address = 255;
fncmd.cmd = REMOTE_CMD_FADE_RGB;
fncmd.step = 25;
fncmd.delay = 0;
fn_send(fd, (struct remote_msg_t *) &fncmd);
}
int main(int argc, char *argv[]) {
/* The sample type to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = SAMPLING_RATE,
.channels = 1
};
pa_simple *s = NULL;
SDL_Surface *screen = NULL;
SDL_Event event;
int error, counter = 0, fd = -1;
uint32_t level;
int16_t * pcm_data;
complex * fft_data;
fftw_plan fft_plan;
/* init fnordlichts */
if (argc > 1) {
fd = open(argv[1], O_RDWR | O_NOCTTY);
if (fd < 0) {
perror(argv[0]);
exit(-1);
}
fn_init(fd);
}
/* init screen & window */
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
exit(-1);
}
/* open sdl window */
SDL_WM_SetCaption(TITLE, NULL);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE);
if (screen == NULL) {
fprintf(stderr, "Unable to set video: %s\n", SDL_GetError());
exit(-1);
}
/* init fftw & get buffers*/
pcm_data = (int16_t *) malloc(BUFFER_SIZE);
fft_data = (complex *) fftw_malloc(BUFFER_SIZE * sizeof (complex));
fft_plan = fftw_plan_dft_1d(BUFFER_SIZE, fft_data, fft_data, FFTW_FORWARD, 0);
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, TITLE, PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
exit(-1);
}
pa_simple_flush(s, &error); /* flush audio buffer */
while (1) {
counter++;
/* handle SDL events */
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
printf("Good bye!\n");
exit(0);
}
}
/* read PCM audio data */
if (pa_simple_read(s, pcm_data, BUFFER_SIZE, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
exit(-1);
}
/* analyse audio data */
uint16_t index, max = 0;
uint32_t sum = 0;
float level;
for (index = 0; index < BUFFER_SIZE; index++) {
sum += abs(pcm_data[index]);
if (abs(pcm_data[index]) > max) max = pcm_data[index];
fft_data[index] = (double) pcm_data[index];
}
/* execute fftw plan */
fftw_execute(fft_plan);
level = (float) sum / (BUFFER_SIZE * 32767);
show_spectrum(screen, (complex *) fft_data);
//show_level(screen, level);
//fade_level(fd, level);
//printf("level: %f \tsum: %d\t max: %d\n", level, sum, max);
}
/* housekeeping */
SDL_Quit();
free(pcm_data);
fftw_free(fft_data);
fftw_destroy(fft_plan);
fftw_cleanup();
return 0;
}

View file

@ -1 +0,0 @@
This is a proof of concept piece of software. Its working for me. Feel free to add options and functions..

Binary file not shown.

View file

@ -1,128 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <math.h>
#include "../libfn.h"
#include <termios.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/gccmacro.h>
#define BUFFER_SIZE 1024
#define SCREEN_WIDTH 500
#define SCREEN_HEIGHT (SCREEN_WIDTH / 20)
#define TITLE "fnordlicht vumeter"
#define DECAY 0.7
void show_level(SDL_Surface *dst, float level) {
SDL_Rect rect = dst->clip_rect;;
Uint32 color = SDL_MapRGB(dst->format, 0xff, 0, 0);
Uint32 background = SDL_MapRGB(dst->format, 0, 0, 0);
SDL_FillRect(dst, &rect, background);
rect.w *= level;
SDL_FillRect(dst, &rect, color);
SDL_Flip(dst);
}
int main(int argc, char*argv[]) {
/* The sample type to use */
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 1
};
pa_simple *s = NULL;
SDL_Surface *screen = NULL;
SDL_Event event;
int error, counter = 0;
uint32_t level;
int16_t buffer[BUFFER_SIZE];
/* init fnordlichts */
struct remote_msg_fade_rgb_t fncmd;
int fd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);
if (fd < 0) {
perror("/dev/ttyUSB0");
exit(-1);
}
memset(&fncmd, 0, sizeof (struct remote_msg_t));
fn_init(fd);
fncmd.address = 255;
fncmd.cmd = REMOTE_CMD_FADE_RGB;
fncmd.step = 25;
fncmd.delay = 0;
/* init screen & window */
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError());
exit(-1);
}
SDL_WM_SetCaption(TITLE, NULL);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 16, SDL_SWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE);
if (screen == NULL) {
fprintf(stderr, "Unable to set 640x480 video: %s\n", SDL_GetError());
exit(-1);
}
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, TITLE, PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
exit(-1);
}
pa_simple_flush(s, &error); /* flush audio buffer */
while (1) {
counter++;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
printf("Good bye!\n");
exit(0);
}
}
if (pa_simple_read(s, buffer, sizeof(buffer), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
exit(-1);
}
int16_t * pos, max = 0;
uint32_t sum = 0;
float level;
for (pos = buffer; pos - buffer < BUFFER_SIZE; pos++) {
sum += abs(*pos);
if (abs(*pos) > max) max = abs(*pos);
}
level = (float) sum / (BUFFER_SIZE * 32767) * 1.7;
if (level > 1) level = 1;
if (counter % (3 * 44100 / 1024) == 0) {
fn_sync(fd);
pa_simple_flush(s, &error);
printf("synced & flushed\n");
}
show_level(screen, level);
fncmd.color.red = fncmd.color.green = fncmd.color.blue = (uint8_t) 255.0 * level;
fn_send(fd, (struct remote_msg_t *) &fncmd);
printf("level: %d (%f) \tsum: %d\t max: %d\n", fncmd.color.red, level, sum, max);
}
/* housekeeping */
SDL_Quit();
}

Binary file not shown.

View file

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.0"
width="428.08844"
height="334.96362"
viewBox="0 0 6076.8403 4773.6932"
id="svg2"
style="fill-rule:evenodd"
inkscape:version="0.48.0 r9654"
sodipodi:docname="Lindos1.svg"
inkscape:output_extension="org.inkscape.output.svg.inkscape">
<metadata
id="metadata658">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1440"
inkscape:window-height="850"
id="namedview656"
showgrid="false"
inkscape:zoom="1.4824748"
inkscape:cx="90.784352"
inkscape:cy="140.87734"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<defs
id="defs657">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 316 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="704 : 316 : 1"
inkscape:persp3d-origin="352 : 211 : 1"
id="perspective660" />
</defs>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g2023"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g2025"
style="fill:#000000;stroke:none">
<text
id="text2027">
<tspan
y="5515"
id="tspan2029" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g2669"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g2671"
style="fill:#000000;stroke:none">
<text
id="text2673">
<tspan
y="4625"
id="tspan2675" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g3069"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g3071"
style="fill:#000000;stroke:none">
<text
id="text3073">
<tspan
y="3087"
id="tspan3075" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g3457"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g3459"
style="fill:#000000;stroke:none">
<text
id="text3461">
<tspan
y="3798"
id="tspan3463" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g3857"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g3859"
style="fill:#000000;stroke:none">
<text
id="text3861">
<tspan
y="2316"
id="tspan3863" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g3989"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g3991"
style="fill:#000000;stroke:none">
<text
id="text3993">
<tspan
y="1515"
id="tspan3995" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g5529"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g5531"
style="fill:#000000;stroke:none">
<text
id="text5533">
<tspan
y="4161"
id="tspan5535" />
</text>
</g>
</g>
<g
transform="translate(-2301.5214,-3353.4089)"
id="g5917"
style="font-size:635px;font-weight:400;visibility:visible;font-family:Helvetica">
<g
id="g5919"
style="fill:#000000;stroke:none">
<text
id="text5921">
<tspan
y="3798"
id="tspan5923" />
</text>
</g>
</g>
<text
x="-2049.7148"
y="-496.28244"
id="text5937"
style="font-size:282px;font-weight:700;fill:#000000;visibility:visible;font-family:Helvetica">
<tspan
y="8336.7178"
id="tspan5939" />
</text>
<path
d="m 6054.3735,26.721334 c -14.8173,277.913896 -2.2039,2218.200166 -39.9925,3251.293166 -18.6661,510.3068 -97.2949,804.581 -281.6852,541.3939 -56.8579,-81.1555 -264.9693,-140.7507 -402.6776,-17.538 -185.065,165.5844 -331.169,428.5715 -331.169,428.5715 0,0 -331.168,530.8441 -496.753,516.2337 -165.584,-14.6104 -370.13,-102.2727 -467.532,-194.8052 -97.403,-92.5324 -194.805,-199.6753 -287.338,-185.0649 -92.532,14.6104 -194.805,92.5325 -194.805,92.5325 0,0 -112.013,121.7532 -545.455,14.6103 -433.441,-107.1428 -633.116,-258.1168 -633.116,-258.1168 0,0 -667.208,-394.4806 -1134.741,-1056.8182 0,0 -998.37603,-1178.5714 -1232.1430282,-1724.026"
id="path7772"
style="fill:none;stroke:#ff0000;stroke-width:53.44266891;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscssscsccc" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

@ -6,6 +6,7 @@
#include <string.h>
#include "color.h"
#include "static_programs.h"
#include "remote-proto.h"
#define FN_BAUDRATE B19200

View file

@ -27,6 +27,9 @@
#define REMOTE_SYNC_LEN 15
#define REMOTE_SYNC_BYTE 0x1b
/* number of color-configurations stored in EEPROM */
#define CONFIG_EEPROM_COLORS 60
/* max mode for startup configuration is 1 */
#define REMOTE_STARTUP_MAX_MODE 1
/* maximum parameter size (for copy loop), size of structure storage_config_t,
@ -59,7 +62,7 @@
#define REMOTE_CMD_FLASH 0x86
#define REMOTE_CMD_ENTER_APP 0x87
#define REMOTE_ADDR_BROADCAST 0xff
#define REMOTE_ADDR_BROADCAST 0xff
/* normal commands */
struct remote_msg_t
@ -135,7 +138,7 @@ struct remote_msg_start_program_t
uint8_t address;
uint8_t cmd;
uint8_t script;
// union program_params_t params;
union program_params_t params;
};
struct remote_msg_stop_t

22
replay.eeprom Normal file
View file

@ -0,0 +1,22 @@
# This CSV file is used to easily edit the slots of your fnordlichter
# Author: Steffen Vogel <info@steffenvogel.de>
#
# There are 6 fields:
# slot | slot in the EEPROM (0-59)
# address | address of the fnordlicht on the bus (0-254, 255 for broadcast)
# color | color in hex format (ex. 'ff31A4')
# step | increment step for fading
# delay | delay between steps when fading (in 10ms)
# pause | time to wait before fading to next color (in 100ms)
#
# Lines beginning with a '#' are ignored as comments
#
# https://github.com/fd0/fnordlicht/raw/master/doc/PROTOCOL
#
#slot;address;color;step;delay;pause
0;255;ffffff;30;2;5
1;255;000000;30;2;5
2;255;00ff00;30;2;10
3;255;00ffff;30;2;10
4;255;ffff00;30;2;10

81
static_programs.h Normal file
View file

@ -0,0 +1,81 @@
/* vim:ts=4 sts=4 et tw=80
*
* fnordlicht firmware
*
* for additional information please
* see http://lochraster.org/fnordlichtmini
*
* (c) by Alexander Neumann <alexander@bumpern.de>
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 as published by
* the Free Software Foundation.
*
* This program 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 General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STATIC_PROGRAMS_H
#define __STATIC_PROGRAMS_H
#include <stdint.h>
#include <stdbool.h>
#define STATIC_PROGRAMS_LEN 3
/* configure maximal static program parameter size */
#define PROGRAM_PARAMETER_SIZE 10
/* parameter structures (max 10 bytes) */
struct colorwheel_params_t
{
uint8_t fade_step;
uint8_t fade_delay;
uint8_t fade_sleep;
uint16_t hue_start;
int16_t hue_step;
int8_t add_addr;
uint8_t saturation;
uint8_t value;
};
struct random_params_t
{
uint16_t seed;
uint8_t use_address:1;
uint8_t wait_for_fade:1;
uint8_t reserved:6;
uint8_t fade_step;
uint8_t fade_delay;
uint16_t fade_sleep;
uint8_t saturation;
uint8_t value;
uint8_t min_distance;
};
struct replay_params_t
{
uint8_t start;
uint8_t end;
enum {
REPEAT_NONE = 0,
REPEAT_START = 1,
REPEAT_REVERSE = 2,
} repeat;
};
union program_params_t
{
/* parameters for static programs */
uint8_t raw[PROGRAM_PARAMETER_SIZE];
struct colorwheel_params_t colorwheel;
struct random_params_t random;
struct replay_params_t replay;
};
#endif