2021-05-26 22:29:24 +00:00
|
|
|
import ws281x from 'rpi-ws281x';
|
|
|
|
import * as fs from 'fs';
|
|
|
|
import Logger, { levels } from './logger.js';
|
2021-05-26 01:55:56 +00:00
|
|
|
|
2021-05-26 03:24:00 +00:00
|
|
|
const cfg = JSON.parse(fs.readFileSync('./config.json'));
|
2021-05-26 16:42:22 +00:00
|
|
|
const log = new Logger("lights", cfg.log_level ? levels[cfg.log_level] : levels.INFO);
|
2021-05-26 03:20:35 +00:00
|
|
|
|
2021-05-26 03:51:11 +00:00
|
|
|
const fade_ticks = cfg.fade_ticks || 20;
|
2021-05-26 03:20:35 +00:00
|
|
|
var pixels = new Uint32Array(cfg.leds);
|
|
|
|
var pixel_cache = new Uint32Array(cfg.leds);
|
|
|
|
var next_pattern = new Uint32Array(cfg.leds);
|
2021-05-26 23:44:47 +00:00
|
|
|
const targets = {}
|
2021-05-26 03:18:02 +00:00
|
|
|
var pattern = {}
|
2021-05-26 01:55:56 +00:00
|
|
|
|
2021-05-26 03:46:40 +00:00
|
|
|
function rgb_to_int(r, g, b) {
|
2021-05-26 16:18:38 +00:00
|
|
|
let rgb = r;
|
|
|
|
rgb = (rgb << 8) + g;
|
|
|
|
rgb = (rgb << 8) + b;
|
|
|
|
return rgb;
|
2021-05-26 03:46:40 +00:00
|
|
|
}
|
|
|
|
function int_to_rgb(int) {
|
2021-05-26 16:18:38 +00:00
|
|
|
var r = (int >> 16) & 0xFF;
|
|
|
|
var g = (int >> 8) & 0xFF;
|
|
|
|
var b = (int) & 0xFF;
|
2021-05-26 03:46:40 +00:00
|
|
|
return { r: r, g: g, b: b };
|
|
|
|
}
|
|
|
|
|
2021-05-26 03:18:02 +00:00
|
|
|
ws281x.configure({
|
2021-05-26 03:20:35 +00:00
|
|
|
leds: cfg.leds || 300,
|
|
|
|
brightness: cfg.brightness || 200,
|
|
|
|
gpio: cfg.gpio || 18,
|
|
|
|
stripType: cfg.type || 'grb'
|
2021-05-26 03:18:02 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
export function set_pattern(pat) {
|
2021-05-26 23:44:47 +00:00
|
|
|
log.debug("pattern set");
|
|
|
|
pattern = pat;
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
|
|
|
|
2021-05-27 18:21:54 +00:00
|
|
|
export const functions = {
|
2021-05-26 23:44:47 +00:00
|
|
|
random: (index, args, prev) => { return Math.floor(Math.random() * 256) },
|
|
|
|
constant: (index, args, prev) => { return args; },
|
2021-05-26 23:52:58 +00:00
|
|
|
modulo: (index, args, prev) => { return prev % args; },
|
|
|
|
move: (index, args, prev) => { targets[args] = prev; return prev; },
|
2021-05-27 18:19:20 +00:00
|
|
|
swap: (index, args, prev) => { let temp = targets[args]; targets[args] = prev; return temp; },
|
|
|
|
add: (index, args, prev) => { return prev + args; },
|
|
|
|
subtract: (index, args, prev) => { return prev - args; }
|
2021-05-26 22:20:56 +00:00
|
|
|
}
|
|
|
|
|
2021-05-26 03:18:02 +00:00
|
|
|
function tick_pattern() {
|
|
|
|
// do the parsing stuff here
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug("TICKING PATTERN")
|
2021-05-26 03:18:02 +00:00
|
|
|
|
2021-05-26 22:23:39 +00:00
|
|
|
if (Object.keys(pattern).length > 0) {
|
2021-05-26 23:44:47 +00:00
|
|
|
for (let i = 0; i < cfg.leds; i++) {
|
|
|
|
for (let command of pattern) {
|
|
|
|
if (command.valid) {
|
|
|
|
let instruction = command.instruction;
|
2021-05-26 23:56:14 +00:00
|
|
|
let name = instruction.name.toLowerCase();
|
|
|
|
if (functions[name]) {
|
2021-05-26 23:44:47 +00:00
|
|
|
for (let target of instruction.args) {
|
|
|
|
let channel = target.channel;
|
|
|
|
if (!targets[channel]) {
|
|
|
|
targets[channel] = 0;
|
|
|
|
}
|
2021-05-26 23:56:14 +00:00
|
|
|
log.debug("targets " + name + " " + channel + " " + target.arg);
|
2021-05-26 23:44:47 +00:00
|
|
|
targets[channel] =
|
2021-05-26 23:56:14 +00:00
|
|
|
functions[name](i, target.arg, targets[channel]);
|
2021-05-26 23:44:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-26 23:56:14 +00:00
|
|
|
|
|
|
|
log.debug(`next: ${targets["r"]}, ${targets["g"]}, ${targets["b"]}`);
|
|
|
|
next_pattern[i] = rgb_to_int(targets["r"] || 0, targets["g"] || 0, targets["b"] || 0);
|
2021-05-27 00:15:57 +00:00
|
|
|
targets["r"] = 0; targets["g"] = 0; targets["b"] = 0;
|
2021-05-26 23:44:47 +00:00
|
|
|
}
|
2021-05-26 22:20:56 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
getRandom()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRandom() {
|
2021-05-26 03:20:35 +00:00
|
|
|
for (let i = 0; i < cfg.leds; i++) {
|
2021-05-26 16:15:04 +00:00
|
|
|
var r = Math.floor(Math.random() * 256)
|
|
|
|
var g = Math.floor(Math.random() * 256);
|
|
|
|
var b = Math.floor(Math.random() * 256);
|
|
|
|
next_pattern[i] = rgb_to_int(r, g, b);
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function tick() {
|
|
|
|
var changed = false;
|
2021-05-26 03:20:35 +00:00
|
|
|
for (let i = 0; i < cfg.leds; i++) {
|
2021-05-26 03:18:02 +00:00
|
|
|
if (next_pattern[i] != pixels[i]) {
|
2021-05-26 14:24:57 +00:00
|
|
|
if (next_pattern[i] == pixel_cache[i]) {
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug("INCONGRUENCE WITH " + i);
|
2021-05-26 14:56:54 +00:00
|
|
|
pixels[i] = next_pattern[i];
|
2021-05-26 14:24:57 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
changed = true;
|
|
|
|
fade(i);
|
|
|
|
}
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
2021-05-26 16:11:41 +00:00
|
|
|
else if (pixel_cache[i] != pixels[i]) {
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug("PATTERN NOT STORED " + i);
|
2021-05-26 16:11:41 +00:00
|
|
|
pixel_cache[i] = pixels[i];
|
2021-05-26 14:56:54 +00:00
|
|
|
}
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
|
|
|
if (!changed) {
|
|
|
|
tick_pattern();
|
|
|
|
}
|
2021-05-26 16:09:37 +00:00
|
|
|
else {
|
|
|
|
ws281x.render(pixels);
|
|
|
|
}
|
2021-05-26 22:28:52 +00:00
|
|
|
//ws281x.sleep(cfg.sleep_time || 500);
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function fade(index) {
|
2021-05-26 03:46:40 +00:00
|
|
|
var original = int_to_rgb(pixel_cache[index]);
|
|
|
|
var current = int_to_rgb(pixels[index]);
|
|
|
|
var final = int_to_rgb(next_pattern[index]);
|
|
|
|
var diff_r = final.r - original.r;
|
2021-05-26 03:57:35 +00:00
|
|
|
var diff_cr = current.r - original.r;
|
2021-05-26 03:46:40 +00:00
|
|
|
var diff_g = final.g - original.g;
|
2021-05-26 14:48:49 +00:00
|
|
|
var diff_cg = current.g - original.g;
|
2021-05-26 03:46:40 +00:00
|
|
|
var diff_b = final.b - original.b;
|
2021-05-26 14:48:49 +00:00
|
|
|
var diff_cb = current.b - original.b;
|
2021-05-26 16:25:49 +00:00
|
|
|
var sign_r = diff_r === Math.abs(diff_r) ? 1 : -1;
|
|
|
|
var sign_g = diff_g === Math.abs(diff_g) ? 1 : -1;
|
|
|
|
var sign_b = diff_b === Math.abs(diff_b) ? 1 : -1;
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug(`${diff_r} ${sign_r} ${diff_g} ${sign_g} ${diff_b} ${sign_b}`);
|
2021-05-26 16:25:49 +00:00
|
|
|
var interval_r = sign_r * Math.ceil(Math.abs(diff_r) / fade_ticks);
|
|
|
|
var interval_g = sign_g * Math.ceil(Math.abs(diff_g) / fade_ticks);
|
|
|
|
var interval_b = sign_b * Math.ceil(Math.abs(diff_b) / fade_ticks);
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug(`r${Math.abs(diff_r) / fade_ticks} ${Math.ceil(Math.abs(diff_r) / fade_ticks)}` +
|
2021-05-26 16:32:31 +00:00
|
|
|
`g${Math.abs(diff_g) / fade_ticks} ${Math.ceil(Math.abs(diff_g) / fade_ticks)}` +
|
|
|
|
`b${Math.abs(diff_b) / fade_ticks} ${Math.ceil(Math.abs(diff_b) / fade_ticks)}`);
|
2021-05-26 03:46:40 +00:00
|
|
|
|
2021-05-26 16:27:48 +00:00
|
|
|
var current_tick_r = Math.floor(Math.abs(diff_cr / diff_r) * fade_ticks);
|
|
|
|
var current_tick_g = Math.floor(Math.abs(diff_cg / diff_g) * fade_ticks);
|
|
|
|
var current_tick_b = Math.floor(Math.abs(diff_cb / diff_b) * fade_ticks);
|
2021-05-26 15:01:34 +00:00
|
|
|
if (diff_r == 0 ||
|
2021-05-26 16:39:01 +00:00
|
|
|
(sign_r == 1 && current.r + interval_r >= final.r) ||
|
|
|
|
(sign_r == -1 && current.r + interval_r <= final.r) ||
|
2021-05-26 15:01:34 +00:00
|
|
|
current_tick_r + 1 >= fade_ticks) {
|
2021-05-26 14:48:49 +00:00
|
|
|
current.r = final.r;
|
|
|
|
interval_r = 0;
|
2021-05-26 14:53:17 +00:00
|
|
|
current_tick_r = fade_ticks;
|
2021-05-26 14:48:49 +00:00
|
|
|
}
|
2021-05-26 15:01:34 +00:00
|
|
|
if (diff_g == 0 ||
|
2021-05-26 16:39:01 +00:00
|
|
|
(sign_g == 1 && current.g + interval_g >= final.g) ||
|
|
|
|
(sign_g == -1 && current.g + interval_g <= final.g) ||
|
2021-05-26 15:01:34 +00:00
|
|
|
current_tick_g + 1 >= fade_ticks) {
|
2021-05-26 14:48:49 +00:00
|
|
|
current.g = final.g;
|
|
|
|
interval_g = 0;
|
2021-05-26 14:53:17 +00:00
|
|
|
current_tick_g = fade_ticks;
|
2021-05-26 14:48:49 +00:00
|
|
|
}
|
2021-05-26 15:01:34 +00:00
|
|
|
if (diff_b == 0 ||
|
2021-05-26 16:39:01 +00:00
|
|
|
(sign_b == 1 && current.b + interval_b >= final.b) ||
|
|
|
|
(sign_b == -1 && current.b + interval_b <= final.b) ||
|
2021-05-26 15:01:34 +00:00
|
|
|
current_tick_b + 1 >= fade_ticks) {
|
2021-05-26 14:48:49 +00:00
|
|
|
current.b = final.b;
|
|
|
|
interval_b = 0;
|
2021-05-26 14:53:17 +00:00
|
|
|
current_tick_b = fade_ticks;
|
2021-05-26 14:48:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (current_tick_r + 1 >= fade_ticks &&
|
|
|
|
current_tick_g + 1 >= fade_ticks &&
|
|
|
|
current_tick_b + 1 >= fade_ticks) {
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug("FINISHED");
|
2021-05-26 03:46:40 +00:00
|
|
|
pixel_cache[index] = next_pattern[index];
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-05-26 03:46:40 +00:00
|
|
|
pixels[index] = rgb_to_int(current.r + interval_r,
|
|
|
|
current.g + interval_g,
|
|
|
|
current.b + interval_b);
|
2021-05-26 16:10:16 +00:00
|
|
|
let prev = int_to_rgb(pixel_cache[index]);
|
2021-05-26 16:42:22 +00:00
|
|
|
log.debug(`${current_tick_r} ${current_tick_g} ${current_tick_b}: `
|
2021-05-26 15:03:27 +00:00
|
|
|
+ `CURRENT COLOR: ${current.r} ${current.g} ${current.b} NEW COLOR: ${current.r + interval_r} ${current.g + interval_g} ${current.b + interval_b} ` +
|
|
|
|
`FINAL: ${final.r} ${final.g} ${final.b} ` +
|
2021-05-26 16:09:37 +00:00
|
|
|
`\nINTERVAL: ${interval_r} ${interval_g} ${interval_b} ` +
|
2021-05-26 16:10:16 +00:00
|
|
|
`PREVIOUS: ${prev.r} ${prev.g} ${prev.b}`);
|
2021-05-26 03:18:02 +00:00
|
|
|
}
|
2021-05-26 01:55:56 +00:00
|
|
|
}
|