leds/lights.js

309 lines
8.0 KiB
JavaScript
Raw Normal View History

2021-10-08 05:02:56 +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-10-08 05:02:56 +00:00
const cfg = JSON.parse(fs.readFileSync("./config.json"));
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-10-08 05:02:56 +00:00
var num_ticks = 0;
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-10-08 05:02:56 +00:00
const targets = {};
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) {
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-10-08 05:02:56 +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,
2021-10-08 05:02:56 +00:00
stripType: cfg.type || "grb",
2021-05-26 03:18:02 +00:00
});
export function set_pattern(pat) {
2021-10-08 05:02:56 +00:00
pattern = pat;
2021-05-26 23:44:47 +00:00
log.debug("pattern set");
2021-05-26 03:18:02 +00:00
}
2021-06-05 01:36:40 +00:00
export class Function {
func;
options;
constructor(func, options) {
this.func = func;
this.options = options;
}
2021-05-26 22:20:56 +00:00
}
2021-06-05 01:36:40 +00:00
export const functions = {
random: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
return Math.floor(Math.random() * 256);
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: false,
2021-10-08 05:02:56 +00:00
convert_args: false,
2021-06-05 01:36:40 +00:00
}
),
2021-06-05 02:12:08 +00:00
move: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
return arg2;
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: true,
2021-10-08 05:02:56 +00:00
convert_args: true,
}
),
2021-06-05 01:36:40 +00:00
modulo: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
return arg1 % arg2;
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: true,
2021-10-08 05:02:56 +00:00
convert_args: true,
}
),
2021-06-05 01:36:40 +00:00
swap: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
let temp = targets[arg2];
targets[arg2] = targets[arg1];
return temp;
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: true,
2021-10-08 05:02:56 +00:00
convert_args: false,
}
),
2021-06-05 01:36:40 +00:00
add: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
return arg1 + arg2;
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: true,
2021-10-08 05:02:56 +00:00
convert_args: true,
}
),
2021-06-05 01:36:40 +00:00
subtract: new Function(
2021-10-08 05:02:56 +00:00
(index, arg1, arg2) => {
return arg1 - arg2;
},
2021-06-05 01:36:40 +00:00
{
requires_arg1: true,
2021-06-05 02:02:13 +00:00
requires_arg2: true,
2021-10-08 05:02:56 +00:00
convert_args: true,
}
),
stagger: new Function(
(index, arg1, arg2) => {
return (index + arg2) % 255;
},
{
requires_arg1: true,
requires_arg2: true,
convert_args: true,
2021-10-08 05:14:04 +00:00
}
),
slide: new Function(
(index, arg1, arg2) => {
return (num_ticks + arg2) % 255;
2021-10-08 05:02:56 +00:00
},
2021-10-08 05:14:04 +00:00
{
requires_arg1: true,
requires_arg2: true,
convert_args: true,
}
2021-10-08 05:02:56 +00:00
),
};
2021-05-27 18:33:12 +00:00
2021-05-26 03:18:02 +00:00
function tick_pattern() {
// do the parsing stuff here
2021-10-08 05:02:56 +00:00
log.debug("TICKING PATTERN");
2021-05-26 03:18:02 +00:00
2021-06-05 02:21:20 +00:00
let parsed = pattern.parsed;
2021-06-05 02:35:04 +00:00
log.debug(`${JSON.stringify(pattern)} ${JSON.stringify(parsed)}`);
2021-06-05 02:21:20 +00:00
2021-06-05 02:21:54 +00:00
if (parsed && parsed.length > 0) {
2021-05-26 23:44:47 +00:00
for (let i = 0; i < cfg.leds; i++) {
2021-06-05 02:21:20 +00:00
for (let id in parsed) {
let command = parsed[id];
2021-06-05 02:29:55 +00:00
log.debug(`pattern ${id} ${command}`);
2021-06-05 02:17:52 +00:00
let name = command["command"];
2021-06-05 02:35:04 +00:00
log.debug(`${name} matches: ${functions[name]}`);
2021-06-05 02:16:00 +00:00
if (functions[name] != undefined) {
2021-10-08 05:02:56 +00:00
if (
functions[name].options &&
functions[name].options["convert_args"]
) {
2021-06-05 02:02:13 +00:00
let param_arg1 = parseInt(command.arg1) || targets[command.arg1];
let param_arg2 = parseInt(command.arg2) || targets[command.arg2];
2021-06-05 02:12:08 +00:00
let result = functions[name].func(i, param_arg1, param_arg2);
2021-10-08 05:02:56 +00:00
log.debug(
`convert ${command.arg1} ${param_arg1} ${command.arg2} ${param_arg2} ${result}`
);
2021-06-05 02:12:08 +00:00
targets[command.arg1] = result;
2021-10-08 05:02:56 +00:00
} else {
2021-06-05 02:12:08 +00:00
let result = functions[name].func(i, command.arg1, command.arg2);
2021-06-05 02:35:04 +00:00
log.debug(`no convert ${command.arg1} = ${result}`);
2021-06-05 02:12:08 +00:00
targets[command.arg1] = result;
2021-06-05 02:02:13 +00:00
}
2021-05-26 23:44:47 +00:00
}
}
2021-06-05 02:35:04 +00:00
log.debug(`next: ${targets["r"]}, ${targets["g"]}, ${targets["b"]}`);
2021-10-08 05:02:56 +00:00
next_pattern[i] = rgb_to_int(
targets["r"] || 0,
targets["g"] || 0,
targets["b"] || 0
);
targets["r"] = 0;
targets["g"] = 0;
targets["b"] = 0;
2021-05-26 23:44:47 +00:00
}
2021-10-08 05:02:56 +00:00
} else {
getRandom();
2021-05-26 22:20:56 +00:00
}
2021-10-08 05:02:56 +00:00
num_ticks++;
2021-05-26 22:20:56 +00:00
}
function getRandom() {
2021-05-26 03:20:35 +00:00
for (let i = 0; i < cfg.leds; i++) {
2021-10-08 05:02:56 +00:00
var r = Math.floor(Math.random() * 256);
2021-05-26 16:15:04 +00:00
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-10-08 05:02:56 +00:00
} else {
2021-05-26 14:24:57 +00:00
changed = true;
fade(i);
}
2021-10-08 05:02:56 +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-10-08 05:02:56 +00:00
} else {
2021-05-26 16:09:37 +00:00
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;
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}`);
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-10-08 05:02:56 +00:00
log.debug(
`r${Math.abs(diff_r) / fade_ticks} ${Math.ceil(
Math.abs(diff_r) / fade_ticks
)}` +
`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-10-08 05:02:56 +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-10-08 05:02:56 +00:00
current_tick_r + 1 >= fade_ticks
) {
2021-05-26 14:48:49 +00:00
current.r = final.r;
interval_r = 0;
current_tick_r = fade_ticks;
2021-05-26 14:48:49 +00:00
}
2021-10-08 05:02:56 +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-10-08 05:02:56 +00:00
current_tick_g + 1 >= fade_ticks
) {
2021-05-26 14:48:49 +00:00
current.g = final.g;
interval_g = 0;
current_tick_g = fade_ticks;
2021-05-26 14:48:49 +00:00
}
2021-10-08 05:02:56 +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-10-08 05:02:56 +00:00
current_tick_b + 1 >= fade_ticks
) {
2021-05-26 14:48:49 +00:00
current.b = final.b;
interval_b = 0;
current_tick_b = fade_ticks;
2021-05-26 14:48:49 +00:00
}
2021-10-08 05:02:56 +00:00
if (
current_tick_r + 1 >= fade_ticks &&
2021-05-26 14:48:49 +00:00
current_tick_g + 1 >= fade_ticks &&
2021-10-08 05:02:56 +00:00
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-10-08 05:02:56 +00:00
} else {
pixels[index] = rgb_to_int(
current.r + interval_r,
2021-05-26 03:46:40 +00:00
current.g + interval_g,
2021-10-08 05:02:56 +00:00
current.b + interval_b
);
let prev = int_to_rgb(pixel_cache[index]);
2021-10-08 05:02:56 +00:00
log.debug(
`${current_tick_r} ${current_tick_g} ${current_tick_b}: ` +
`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} ` +
`\nINTERVAL: ${interval_r} ${interval_g} ${interval_b} ` +
`PREVIOUS: ${prev.r} ${prev.g} ${prev.b}`
);
2021-05-26 03:18:02 +00:00
}
2021-10-08 05:02:56 +00:00
}