1st version with c implementation of state engine
This commit is contained in:
parent
f6a3f4a5f3
commit
e2209504da
|
@ -0,0 +1,352 @@
|
||||||
|
/* animateLights.js
|
||||||
|
* LED animation for house lightning
|
||||||
|
*/
|
||||||
|
|
||||||
|
load('api_rpc.js');
|
||||||
|
load("api_neopixel.js");
|
||||||
|
load("api_file.js");
|
||||||
|
|
||||||
|
let floor = ffi('double floor(double)');
|
||||||
|
|
||||||
|
let colors = {
|
||||||
|
black: {r:0, g:0, b:0},
|
||||||
|
red: {r:255, g:0, b:0},
|
||||||
|
green: {r:0, g:255, b:0},
|
||||||
|
blue: {r:0, g:0, b:255},
|
||||||
|
yellow: {r:255, g:255, b:0},
|
||||||
|
orange: {r:255, g:127, b:0},
|
||||||
|
purple: {r:255, g:0, b:255},
|
||||||
|
white: {r:255, g:255, b:255},
|
||||||
|
dimmedWhite: {r:50, g:50, b:50}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let LightAnimation = {
|
||||||
|
numberOfBulbs: 22,
|
||||||
|
ledPin: 2,
|
||||||
|
that: this,
|
||||||
|
running: false,
|
||||||
|
mode: "demo", // "demo", "stop", "animate"
|
||||||
|
generalBrightness: 30, // percentage, value 0..100
|
||||||
|
description: "Default house lightning",
|
||||||
|
defaultColor: colors.dimmedWhite,
|
||||||
|
defaultMode: "demo",
|
||||||
|
tickResolution: 100, // number of milliseconds between ticks
|
||||||
|
currentTick: 0,
|
||||||
|
animationConfig: {}, // overwritten when loading animationConfig.js
|
||||||
|
colorSequence: [
|
||||||
|
colors.dimmedWhite,
|
||||||
|
colors.red,
|
||||||
|
colors.green,
|
||||||
|
colors.blue,
|
||||||
|
colors.yellow,
|
||||||
|
colors.orange,
|
||||||
|
colors.purple,
|
||||||
|
colors.red,
|
||||||
|
colors.red,
|
||||||
|
colors.green,
|
||||||
|
colors.green,
|
||||||
|
colors.white,
|
||||||
|
colors.white
|
||||||
|
],
|
||||||
|
adjustBrightness: function (value) { return floor(value / LightAnimation.generalBrightness); },
|
||||||
|
changeMode: function (newMode) {
|
||||||
|
if (newMode === "animate") {
|
||||||
|
LightAnimation.running = true;
|
||||||
|
LightAnimation.mode = "start-animate"; // through transient mode start-animate to animate
|
||||||
|
} else
|
||||||
|
if (newMode === "stop") {
|
||||||
|
LightAnimation.running = true; // first we want to turn off LEDs, thus needs stop-mode but running
|
||||||
|
LightAnimation.mode = "stop";
|
||||||
|
} else
|
||||||
|
if (newMode === "demo") {
|
||||||
|
LightAnimation.mode = "demo";
|
||||||
|
LightAnimation.running = true;
|
||||||
|
} else
|
||||||
|
if (newMode === "cycle") {
|
||||||
|
LightAnimation.mode = "cycle";
|
||||||
|
LightAnimation.running = true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
print("IGNORED: changeMode called with invalid mode = ", newMode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateAnimationConfig: function (config) {
|
||||||
|
LightAnimation.animationConfig = JSON.parse(config);
|
||||||
|
return LightAnimation.verifyAnimationConfig();
|
||||||
|
},
|
||||||
|
verifyAnimationConfig: function() {
|
||||||
|
if (LightAnimation.animationConfig.comment === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / comment not found'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / lights definition not found'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[0] === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / lights is not an array?'};
|
||||||
|
}
|
||||||
|
let i;
|
||||||
|
for (i=0; i<LightAnimation.animationConfig.lights.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.lights[i].level === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / level missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].room === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / room missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / on missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on[0] === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / on not an array in lights definition?'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on.length !== 3) {
|
||||||
|
return {error: 'config is incomplete or invalid / on must have 3 elements for R/G/B in lights definition?'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].id === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / id missing in lights definition'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {result: 'ok'};
|
||||||
|
},
|
||||||
|
findNextAnimationStep: function (lightIndex, afterStep) {
|
||||||
|
let i = afterStep + 1;
|
||||||
|
|
||||||
|
for (i=afterStep + 1; i<LightAnimation.animationConfig.animation.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].lightsIndex === lightIndex)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// nothing found, thus start from the beginning
|
||||||
|
for (i=0; i<=afterStep; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].lightsIndex === lightIndex)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// nothing found again, thus it seems, there is no config for this lightIndex
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// load("animationConfig.js", LightAnimation);
|
||||||
|
load("animationConfig.js");
|
||||||
|
print(LightAnimation.verifyAnimationConfig());
|
||||||
|
|
||||||
|
print("Initialize neoPixel");
|
||||||
|
let colorOrder = NeoPixel.GRB;
|
||||||
|
let strip = NeoPixel.create(LightAnimation.ledPin, LightAnimation.numberOfBulbs, colorOrder);
|
||||||
|
let i = 0;
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
// initialize test pattern
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
if (i % 2) {
|
||||||
|
strip.setPixel(i, 80, 20, 20);
|
||||||
|
} else {
|
||||||
|
strip.setPixel(i, 20, 80, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
return; // @TODO
|
||||||
|
|
||||||
|
// Add RPC handlers
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.Start', function(args) {
|
||||||
|
LightAnimation.changeMOde("animate");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.Stop', function(args) {
|
||||||
|
LightAnimation.changeMode("stop");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.DemoMode', function(args) {
|
||||||
|
LightAnimation.changeMode("demo");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.CycleMode', function(args) {
|
||||||
|
LightAnimation.changeMOde("cycle");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.GetAnimationConfig', function(args) {
|
||||||
|
return LightAnimation.animationConfig;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.SetAnimationConfig', function(args) {
|
||||||
|
if (args !== undefined && args.config !== undefined) {
|
||||||
|
return LightAnimation.updateAnimationConfig(args.config);
|
||||||
|
} else {
|
||||||
|
return {error: 'parameter config is required'};
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
function demoAnimation(strip, shiftBy) {
|
||||||
|
let i=0, colorIndex=0, color;
|
||||||
|
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
colorIndex = shiftBy % LightAnimation.colorSequence.length;
|
||||||
|
color = LightAnimation.colorSequence[colorIndex];
|
||||||
|
strip.setPixel(i, LightAnimation.adjustBrightness(color.r), LightAnimation.adjustBrightness(color.g), LightAnimation.adjustBrightness(color.b));
|
||||||
|
// print("runNeo shift =", shiftBy, ", colorIndex =", colorIndex, ", r =", color.r, ", g =", color.g, ", b =", color.b);
|
||||||
|
shiftBy++;
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize module
|
||||||
|
let configString = "";
|
||||||
|
/*configString = File.read("lights.cfg");
|
||||||
|
if (configString === "" || configString === null) configString = "***default***";
|
||||||
|
// print("configString = ", configString);
|
||||||
|
LightAnimation.animationConfig = JSON.parse(configString);
|
||||||
|
configString = null;*/
|
||||||
|
|
||||||
|
// LightAnimation.animationConfig = animationConfig;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// print("LightAnimation.animationConfig = ", JSON.stringify(LightAnimation.animationConfig));
|
||||||
|
print("Animation length: ", LightAnimation.animationConfig.animation.length);
|
||||||
|
// initialize internal data structure
|
||||||
|
let LEDstate = [{"index":0}]; //LightAnimation.numberOfBulbs];
|
||||||
|
let defaultLEDstate = {
|
||||||
|
index: 0, // LED #
|
||||||
|
nextTick: 0, // next action necessary on tick #
|
||||||
|
rampTickDelta: 0, // to be added on nextTick for Ramp-Animation
|
||||||
|
mode: "off", // "on", "off", "blink", "tv", "fire", "welding"
|
||||||
|
currentLEDCtrl: 0, // index on LEDCtrl[LEDnum][...]
|
||||||
|
numLEDCtrl: 0, // number of LEDCtrl entries for this bulb
|
||||||
|
modeStep: 0, // dependent on mode to ctrl animation details
|
||||||
|
clockEndHours: 23, // time, this animation should end
|
||||||
|
clockEndMinutes: 59,
|
||||||
|
currentRed: colors.dimmedWhite.r, currentGreen: colors.dimmedWhite.g, currentBlue: colors.dimmedWhite.b,
|
||||||
|
onRed: colors.dimmedWhite.r, onGreen: colors.dimmedWhite.g, onBlue: colors.dimmedWhite.b,
|
||||||
|
targetRed: 0, targetGreen: 0, targetBlue: 0,
|
||||||
|
currentAnimationStep:0 // animation step in LightAnimation.animationConfig.animation[step]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
print("Init LEDstate", i);
|
||||||
|
LEDstate[i].index = i;
|
||||||
|
LEDstate[i].nextTick = defaultLEDstate.nextTick;
|
||||||
|
LEDstate[i].rampTickDelta = defaultLEDstate.rampTickDelta;
|
||||||
|
LEDstate[i].mode = defaultLEDstate.mode;
|
||||||
|
LEDstate[i].currentLEDCtrl = defaultLEDstate.currentLEDCtrl;
|
||||||
|
LEDstate[i].numLEDCtrl = defaultLEDstate.numLEDCtrl;
|
||||||
|
LEDstate[i].modeStep = defaultLEDstate.modeStep;
|
||||||
|
LEDstate[i].clockEndHours = defaultLEDstate.clockEndHours;
|
||||||
|
LEDstate[i].clockEndMinutes = defaultLEDstate.clockEndMinutes;
|
||||||
|
LEDstate[i].currentRed = defaultLEDstate.currentRed;
|
||||||
|
LEDstate[i].currentGreen = defaultLEDstate.currentGreen;
|
||||||
|
LEDstate[i].currentBlue = defaultLEDstate.currentBlue;
|
||||||
|
LEDstate[i].targetRed = defaultLEDstate.targetRed;
|
||||||
|
LEDstate[i].targetGreen = defaultLEDstate.targetGreen;
|
||||||
|
LEDstate[i].targetBlue = defaultLEDstate.targetBlue;
|
||||||
|
LEDstate[i].onRed = defaultLEDstate.onRed; //LightAnimation.animationConfig.lights.on[0];
|
||||||
|
LEDstate[i].onGreen = defaultLEDstate.onGreen; //LightAnimation.animationConfig.lights.on[1];
|
||||||
|
LEDstate[i].onBlue = defaultLEDstate.onBlue; //LightAnimation.animationConfig.lights.on[2];
|
||||||
|
LEDstate[i].currentAnimationStep = LightAnimation.findNextAnimationStep(i, -1);
|
||||||
|
}
|
||||||
|
// print("LEDstate=", JSON.stringify(LEDstate));
|
||||||
|
|
||||||
|
function animateBulb(bulb, currentTick) {
|
||||||
|
if (LEDstate[bulb].mode === "on") {
|
||||||
|
strip.setPixel(bulb, LightAnimation.adjustBrightness(LEDstate[bulb].currentRed), LightAnimation.adjustBrightness(LEDstate[bulb].currentGreen), LightAnimation.adjustBrightness(LEDstate[bulb].currentBlue));
|
||||||
|
} else
|
||||||
|
if (LEDstate[bulb].mode === "off") {
|
||||||
|
strip.setPixel(bulb, 0, 0, 0);
|
||||||
|
} else print ("animateBulb: mode not yet implemented - ", LEDstate[bulb].mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
let shiftAnimation = 1; // needed for demo mode
|
||||||
|
|
||||||
|
function animateTick() {
|
||||||
|
// started for every single ticks; does all changes necessary according to animation configuration
|
||||||
|
if (LightAnimation.running) {
|
||||||
|
if (LightAnimation.mode === "demo") {
|
||||||
|
demoAnimation(strip, shiftAnimation);
|
||||||
|
shiftAnimation++;
|
||||||
|
// print("*** demo-mode, next shift=", shiftAnimation);
|
||||||
|
} else if (LightAnimation.mode === "stop") {
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
LightAnimation.running = false;
|
||||||
|
print("*** stopped");
|
||||||
|
} else if (LightAnimation.mode === "start-animate") {
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
LightAnimation.running = true;
|
||||||
|
LightAnimation.mode = "animate";
|
||||||
|
print("*** started");
|
||||||
|
} else if (LightAnimation.mode === "animate") {
|
||||||
|
LightAnimation.currentTick++;
|
||||||
|
// do animation step changes
|
||||||
|
for (i=0; i<LightAnimation.animationConfig.animation.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].time === animateTick) {
|
||||||
|
// activate new state
|
||||||
|
let bulbIndex = LightAnimation.animationConfig.animation[i].lightsIndex;
|
||||||
|
let nextAnimationStep = LightAnimation.findNextAnimationStep(bulbIndex, LEDstate[bulbIndex].currentAnimationStep);
|
||||||
|
LEDstate[bulbIndex].currentAnimationStep = nextAnimationStep;
|
||||||
|
LEDstate[bulbIndex].nextTick = LightAnimation.animationConfig.animation[nextAnimationStep].time;
|
||||||
|
LEDstate[bulbIndex].mode = LightAnimation.animationConfig.animation[nextAnimationStep].stepType;
|
||||||
|
LEDstate[bulbIndex].modeStep = 0; // reset to start
|
||||||
|
if (LEDstate[bulbIndex].mode === "off") {
|
||||||
|
LEDstate[bulbIndex].targetRed = 0;
|
||||||
|
LEDstate[bulbIndex].targetGreen = 0;
|
||||||
|
LEDstate[bulbIndex].targetBlue = 0;
|
||||||
|
LEDstate[bulbIndex].currentRed = LEDstate[bulbIndex].targetRed;
|
||||||
|
LEDstate[bulbIndex].currentGreen = LEDstate[bulbIndex].targetGreen;
|
||||||
|
LEDstate[bulbIndex].currentBlue = LEDstate[bulbIndex].targetBlue;
|
||||||
|
} else
|
||||||
|
if (LEDstate[bulbIndex].mode === "on") {
|
||||||
|
LEDstate[bulbIndex].targetRed = LEDstate[bulbIndex].onRed;
|
||||||
|
LEDstate[bulbIndex].targetGreen = LEDstate[bulbIndex].onGreen;
|
||||||
|
LEDstate[bulbIndex].targetBlue = LEDstate[bulbIndex].onBlue;
|
||||||
|
LEDstate[bulbIndex].currentRed = LEDstate[bulbIndex].targetRed;
|
||||||
|
LEDstate[bulbIndex].currentGreen = LEDstate[bulbIndex].targetGreen;
|
||||||
|
LEDstate[bulbIndex].currentBlue = LEDstate[bulbIndex].targetBlue;
|
||||||
|
} else
|
||||||
|
print("ERROR: Unsupported mode in LEDstate[", bulbIndex, "].mode=", LEDstate[bulbIndex].mode);
|
||||||
|
// updateBulbAnimation(bulbIndex, animateTick, LightAnimation.animationConfig.animation[i]);
|
||||||
|
/*
|
||||||
|
if (LightAnimation.animationConfig.animation[i].stepType === "on") {
|
||||||
|
LEDstate[bulbIndex].mode = "on";
|
||||||
|
strip.setPixel(bulbIndex, LEDstate[bulbIndex].defaultColor.r, LEDstate[bulbIndex].defaultColor.g, LEDstate[bulbIndex].defaultColor.b);
|
||||||
|
} else
|
||||||
|
if (LightAnimation.animationConfig.animation[i].stepType === "off") {
|
||||||
|
LEDstate[LightAnimation.lightsIndex].mode = "off";
|
||||||
|
strip.setPixel(LightAnimation.lightsIndex, 0, 0, 0);
|
||||||
|
} else print("*** animation stepType not implemented: ", LightAnimation.animationConfig[i].stepType);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do microanimation
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
animateBulb(i, LightAnimation.currentTick);
|
||||||
|
if (LightAnimation.currentTick >= LEDstate[i].nextTick) {
|
||||||
|
// update this animated bulb
|
||||||
|
}
|
||||||
|
// strip.setPixel called in animateBulb
|
||||||
|
// strip.setPixel(i, LightAnimation.adjustBrightness(LEDstate[i].currentRed), LightAnimation.adjustBrightness(LEDstate[i].currentGreen), LightAnimation.adjustBrightness(LEDstate[i].currentBlue));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
} else print("*** Unknown LightAnimation.mode =", LightAnimation.mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return; // @TODO
|
||||||
|
|
||||||
|
// Show start pattern for 3 seconds, then switch to default colors
|
||||||
|
Timer.set(3000 /* msec */, true /* repeat */, function() {
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
strip.setPixel(i, LightAnimation.defaultColor.r, LightAnimation.defaultColor.g, LightAnimation.defaultColor.b);
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
print("START animation");
|
||||||
|
|
||||||
|
Timer.set(LightAnimation.tickResolution, true /* repeat */, animateTick, null);
|
||||||
|
}, null);
|
|
@ -0,0 +1,371 @@
|
||||||
|
/* animateLights.js
|
||||||
|
* LED animation for house lightning
|
||||||
|
*/
|
||||||
|
|
||||||
|
load('api_rpc.js');
|
||||||
|
load("api_neopixel.js");
|
||||||
|
load("api_file.js");
|
||||||
|
|
||||||
|
let floor = ffi('double floor(double)');
|
||||||
|
|
||||||
|
let colors = {
|
||||||
|
black: {r:0, g:0, b:0},
|
||||||
|
red: {r:255, g:0, b:0},
|
||||||
|
green: {r:0, g:255, b:0},
|
||||||
|
blue: {r:0, g:0, b:255},
|
||||||
|
yellow: {r:255, g:255, b:0},
|
||||||
|
orange: {r:255, g:127, b:0},
|
||||||
|
purple: {r:255, g:0, b:255},
|
||||||
|
white: {r:255, g:255, b:255},
|
||||||
|
dimmedWhite: {r:50, g:50, b:50}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let LightAnimation = {
|
||||||
|
numberOfBulbs: 22,
|
||||||
|
ledPin: 2,
|
||||||
|
that: this,
|
||||||
|
running: false,
|
||||||
|
mode: "demo", // "demo", "stop", "animate"
|
||||||
|
generalBrightness: 30, // percentage, value 0..100
|
||||||
|
description: "Default house lightning",
|
||||||
|
defaultColor: colors.dimmedWhite,
|
||||||
|
defaultMode: "demo",
|
||||||
|
tickResolution: 1000, // number of milliseconds between ticks
|
||||||
|
currentTick: 0,
|
||||||
|
animationConfig: {}, // overwritten when loading animationConfig.js
|
||||||
|
colorSequence: [
|
||||||
|
colors.dimmedWhite,
|
||||||
|
colors.red,
|
||||||
|
colors.green,
|
||||||
|
colors.blue,
|
||||||
|
colors.yellow,
|
||||||
|
colors.orange,
|
||||||
|
colors.purple,
|
||||||
|
colors.red,
|
||||||
|
colors.red,
|
||||||
|
colors.green,
|
||||||
|
colors.green,
|
||||||
|
colors.white,
|
||||||
|
colors.white
|
||||||
|
],
|
||||||
|
adjustBrightness: function (value) { return floor(value / LightAnimation.generalBrightness); },
|
||||||
|
changeMode: function (newMode) {
|
||||||
|
if (newMode === "animate") {
|
||||||
|
LightAnimation.running = true;
|
||||||
|
LightAnimation.mode = "start-animate"; // through transient mode start-animate to animate
|
||||||
|
} else
|
||||||
|
if (newMode === "stop") {
|
||||||
|
LightAnimation.running = true; // first we want to turn off LEDs, thus needs stop-mode but running
|
||||||
|
LightAnimation.mode = "stop";
|
||||||
|
} else
|
||||||
|
if (newMode === "demo") {
|
||||||
|
LightAnimation.mode = "demo";
|
||||||
|
LightAnimation.running = true;
|
||||||
|
} else
|
||||||
|
if (newMode === "cycle") {
|
||||||
|
LightAnimation.mode = "cycle";
|
||||||
|
LightAnimation.running = true;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
print("IGNORED: changeMode called with invalid mode = ", newMode);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateAnimationConfig: function (config) {
|
||||||
|
LightAnimation.animationConfig = JSON.parse(config);
|
||||||
|
return LightAnimation.verifyAnimationConfig();
|
||||||
|
},
|
||||||
|
verifyAnimationConfig: function() {
|
||||||
|
if (LightAnimation.animationConfig.comment === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / comment not found'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / lights definition not found'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[0] === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / lights is not an array?'};
|
||||||
|
}
|
||||||
|
let i;
|
||||||
|
for (i=0; i<LightAnimation.animationConfig.lights.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.lights[i].level === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / level missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].room === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / room missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / on missing in lights definition'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on[0] === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / on not an array in lights definition?'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].on.length !== 3) {
|
||||||
|
return {error: 'config is incomplete or invalid / on must have 3 elements for R/G/B in lights definition?'};
|
||||||
|
}
|
||||||
|
if (LightAnimation.animationConfig.lights[i].id === undefined) {
|
||||||
|
return {error: 'config is incomplete or invalid / id missing in lights definition'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {result: 'ok'};
|
||||||
|
},
|
||||||
|
findNextAnimationStep: function (lightIndex, afterStep) {
|
||||||
|
let i = afterStep + 1;
|
||||||
|
|
||||||
|
for (i=afterStep + 1; i<LightAnimation.animationConfig.animation.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].lightsIndex === lightIndex)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// nothing found, thus start from the beginning
|
||||||
|
for (i=0; i<=afterStep; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].lightsIndex === lightIndex)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
// nothing found again, thus it seems, there is no config for this lightIndex
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
gc(true);
|
||||||
|
// load("animationConfig.js", LightAnimation);
|
||||||
|
load("animationConfig.js");
|
||||||
|
/*
|
||||||
|
LightAnimation.animationConfig = {
|
||||||
|
"comment": "lights.cfg / JSON",
|
||||||
|
"lights": [
|
||||||
|
{ "level":"EG", "room":"Wohnzimmer", "on":[200,200,200], "id":"EG-WoZi" }
|
||||||
|
],
|
||||||
|
"animation": [
|
||||||
|
{ "lightsIndex": 2, "stepType": "on", "time": 1, "comment": "Fr. S. wacht auf" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
// print(LightAnimation.verifyAnimationConfig());
|
||||||
|
gc(true);
|
||||||
|
|
||||||
|
print("Initialize neoPixel");
|
||||||
|
let colorOrder = NeoPixel.GRB;
|
||||||
|
let strip = NeoPixel.create(LightAnimation.ledPin, LightAnimation.numberOfBulbs, colorOrder);
|
||||||
|
let i = 0;
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
// initialize test pattern
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
if (i % 2) {
|
||||||
|
strip.setPixel(i, 80, 20, 20);
|
||||||
|
} else {
|
||||||
|
strip.setPixel(i, 20, 80, 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
|
||||||
|
|
||||||
|
// Add RPC handlers
|
||||||
|
print("Initialize RPC Handlers");
|
||||||
|
RPC.addHandler('HouseLightning.Start', function(args) {
|
||||||
|
LightAnimation.changeMOde("animate");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.Stop', function(args) {
|
||||||
|
LightAnimation.changeMode("stop");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.DemoMode', function(args) {
|
||||||
|
LightAnimation.changeMode("demo");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.CycleMode', function(args) {
|
||||||
|
LightAnimation.changeMOde("cycle");
|
||||||
|
return {result: 'ok'};
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.GetAnimationConfig', function(args) {
|
||||||
|
return LightAnimation.animationConfig;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
RPC.addHandler('HouseLightning.SetAnimationConfig', function(args) {
|
||||||
|
if (args !== undefined && args.config !== undefined) {
|
||||||
|
return LightAnimation.updateAnimationConfig(args.config);
|
||||||
|
} else {
|
||||||
|
return {error: 'parameter config is required'};
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
function demoAnimation(strip, shiftBy) {
|
||||||
|
let i=0, colorIndex=0, color;
|
||||||
|
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
colorIndex = shiftBy % LightAnimation.colorSequence.length;
|
||||||
|
color = LightAnimation.colorSequence[colorIndex];
|
||||||
|
strip.setPixel(i, LightAnimation.adjustBrightness(color.r), LightAnimation.adjustBrightness(color.g), LightAnimation.adjustBrightness(color.b));
|
||||||
|
// print("runNeo shift =", shiftBy, ", colorIndex =", colorIndex, ", r =", color.r, ", g =", color.g, ", b =", color.b);
|
||||||
|
shiftBy++;
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize module
|
||||||
|
let configString = "";
|
||||||
|
/*configString = File.read("lights.cfg");
|
||||||
|
if (configString === "" || configString === null) configString = "***default***";
|
||||||
|
// print("configString = ", configString);
|
||||||
|
LightAnimation.animationConfig = JSON.parse(configString);
|
||||||
|
configString = null;*/
|
||||||
|
|
||||||
|
// LightAnimation.animationConfig = animationConfig;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// print("LightAnimation.animationConfig = ", JSON.stringify(LightAnimation.animationConfig));
|
||||||
|
print("Animation length: ", LightAnimation.animationConfig.animation.length);
|
||||||
|
// initialize internal data structure
|
||||||
|
let defaultLEDstate = {
|
||||||
|
index: 0, // LED #
|
||||||
|
nextTick: 0, // next action necessary on tick #
|
||||||
|
rampTickDelta: 0, // to be added on nextTick for Ramp-Animation
|
||||||
|
mode: "off", // "on", "off", "blink", "tv", "fire", "welding"
|
||||||
|
currentLEDCtrl: 0, // index on LEDCtrl[LEDnum][...]
|
||||||
|
numLEDCtrl: 0, // number of LEDCtrl entries for this bulb
|
||||||
|
modeStep: 0, // dependent on mode to ctrl animation details
|
||||||
|
clockEndHours: 23, // time, this animation should end
|
||||||
|
clockEndMinutes: 59,
|
||||||
|
currentRed: colors.dimmedWhite.r, currentGreen: colors.dimmedWhite.g, currentBlue: colors.dimmedWhite.b,
|
||||||
|
onRed: colors.dimmedWhite.r, onGreen: colors.dimmedWhite.g, onBlue: colors.dimmedWhite.b,
|
||||||
|
targetRed: 0, targetGreen: 0, targetBlue: 0,
|
||||||
|
currentAnimationStep:0 // animation step in LightAnimation.animationConfig.animation[step]
|
||||||
|
};
|
||||||
|
let LEDstate = [];
|
||||||
|
|
||||||
|
|
||||||
|
for (i=0; i<1/*LightAnimation.numberOfBulbs*/; ++i) {
|
||||||
|
gc(true);
|
||||||
|
print("Init LEDstate", i, ", free RAM ", Sys.free_ram());
|
||||||
|
LEDstate[i] =Object.create(defaultLEDstate);
|
||||||
|
LEDstate[i].index = i;
|
||||||
|
LEDstate[i].nextTick = defaultLEDstate.nextTick;
|
||||||
|
LEDstate[i].rampTickDelta = defaultLEDstate.rampTickDelta;
|
||||||
|
LEDstate[i].mode = defaultLEDstate.mode;
|
||||||
|
LEDstate[i].currentLEDCtrl = defaultLEDstate.currentLEDCtrl;
|
||||||
|
LEDstate[i].numLEDCtrl = defaultLEDstate.numLEDCtrl;
|
||||||
|
LEDstate[i].modeStep = defaultLEDstate.modeStep;
|
||||||
|
LEDstate[i].clockEndHours = defaultLEDstate.clockEndHours;
|
||||||
|
LEDstate[i].clockEndMinutes = defaultLEDstate.clockEndMinutes;
|
||||||
|
LEDstate[i].currentRed = defaultLEDstate.currentRed;
|
||||||
|
LEDstate[i].currentGreen = defaultLEDstate.currentGreen;
|
||||||
|
LEDstate[i].currentBlue = defaultLEDstate.currentBlue;
|
||||||
|
LEDstate[i].targetRed = defaultLEDstate.targetRed;
|
||||||
|
LEDstate[i].targetGreen = defaultLEDstate.targetGreen;
|
||||||
|
LEDstate[i].targetBlue = defaultLEDstate.targetBlue;
|
||||||
|
LEDstate[i].onRed = defaultLEDstate.onRed; //LightAnimation.animationConfig.lights.on[0];
|
||||||
|
LEDstate[i].onGreen = defaultLEDstate.onGreen; //LightAnimation.animationConfig.lights.on[1];
|
||||||
|
LEDstate[i].onBlue = defaultLEDstate.onBlue; //LightAnimation.animationConfig.lights.on[2];
|
||||||
|
// LEDstate[i].currentAnimationStep = LightAnimation.findNextAnimationStep(i, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print("LEDstate index0=", LEDstate[0].index, ", index1=", LEDstate[1].index);
|
||||||
|
print("LEDstate=", JSON.stringify(LEDstate));
|
||||||
|
|
||||||
|
function animateBulb(bulb, currentTick) {
|
||||||
|
if (LEDstate[bulb].mode === "on") {
|
||||||
|
strip.setPixel(bulb, LightAnimation.adjustBrightness(LEDstate[bulb].currentRed), LightAnimation.adjustBrightness(LEDstate[bulb].currentGreen), LightAnimation.adjustBrightness(LEDstate[bulb].currentBlue));
|
||||||
|
} else
|
||||||
|
if (LEDstate[bulb].mode === "off") {
|
||||||
|
strip.setPixel(bulb, 0, 0, 0);
|
||||||
|
} else print ("animateBulb: mode not yet implemented - ", LEDstate[bulb].mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
let shiftAnimation = 1; // needed for demo mode
|
||||||
|
|
||||||
|
function animateTick() {
|
||||||
|
// started for every single ticks; does all changes necessary according to animation configuration
|
||||||
|
print("***animate***");
|
||||||
|
return;
|
||||||
|
if (LightAnimation.running) {
|
||||||
|
if (LightAnimation.mode === "demo") {
|
||||||
|
demoAnimation(strip, shiftAnimation);
|
||||||
|
shiftAnimation++;
|
||||||
|
// print("*** demo-mode, next shift=", shiftAnimation);
|
||||||
|
} else if (LightAnimation.mode === "stop") {
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
LightAnimation.running = false;
|
||||||
|
print("*** stopped");
|
||||||
|
} else if (LightAnimation.mode === "start-animate") {
|
||||||
|
strip.clear();
|
||||||
|
strip.show();
|
||||||
|
LightAnimation.running = true;
|
||||||
|
LightAnimation.mode = "animate";
|
||||||
|
print("*** started");
|
||||||
|
} else if (LightAnimation.mode === "animate") {
|
||||||
|
LightAnimation.currentTick++;
|
||||||
|
// do animation step changes
|
||||||
|
for (i=0; i<LightAnimation.animationConfig.animation.length; ++i) {
|
||||||
|
if (LightAnimation.animationConfig.animation[i].time === animateTick) {
|
||||||
|
// activate new state
|
||||||
|
let bulbIndex = LightAnimation.animationConfig.animation[i].lightsIndex;
|
||||||
|
let nextAnimationStep = LightAnimation.findNextAnimationStep(bulbIndex, LEDstate[bulbIndex].currentAnimationStep);
|
||||||
|
LEDstate[bulbIndex].currentAnimationStep = nextAnimationStep;
|
||||||
|
LEDstate[bulbIndex].nextTick = LightAnimation.animationConfig.animation[nextAnimationStep].time;
|
||||||
|
LEDstate[bulbIndex].mode = LightAnimation.animationConfig.animation[nextAnimationStep].stepType;
|
||||||
|
LEDstate[bulbIndex].modeStep = 0; // reset to start
|
||||||
|
if (LEDstate[bulbIndex].mode === "off") {
|
||||||
|
LEDstate[bulbIndex].targetRed = 0;
|
||||||
|
LEDstate[bulbIndex].targetGreen = 0;
|
||||||
|
LEDstate[bulbIndex].targetBlue = 0;
|
||||||
|
LEDstate[bulbIndex].currentRed = LEDstate[bulbIndex].targetRed;
|
||||||
|
LEDstate[bulbIndex].currentGreen = LEDstate[bulbIndex].targetGreen;
|
||||||
|
LEDstate[bulbIndex].currentBlue = LEDstate[bulbIndex].targetBlue;
|
||||||
|
} else
|
||||||
|
if (LEDstate[bulbIndex].mode === "on") {
|
||||||
|
LEDstate[bulbIndex].targetRed = LEDstate[bulbIndex].onRed;
|
||||||
|
LEDstate[bulbIndex].targetGreen = LEDstate[bulbIndex].onGreen;
|
||||||
|
LEDstate[bulbIndex].targetBlue = LEDstate[bulbIndex].onBlue;
|
||||||
|
LEDstate[bulbIndex].currentRed = LEDstate[bulbIndex].targetRed;
|
||||||
|
LEDstate[bulbIndex].currentGreen = LEDstate[bulbIndex].targetGreen;
|
||||||
|
LEDstate[bulbIndex].currentBlue = LEDstate[bulbIndex].targetBlue;
|
||||||
|
} else
|
||||||
|
print("ERROR: Unsupported mode in LEDstate[", bulbIndex, "].mode=", LEDstate[bulbIndex].mode);
|
||||||
|
// updateBulbAnimation(bulbIndex, animateTick, LightAnimation.animationConfig.animation[i]);
|
||||||
|
/*
|
||||||
|
if (LightAnimation.animationConfig.animation[i].stepType === "on") {
|
||||||
|
LEDstate[bulbIndex].mode = "on";
|
||||||
|
strip.setPixel(bulbIndex, LEDstate[bulbIndex].defaultColor.r, LEDstate[bulbIndex].defaultColor.g, LEDstate[bulbIndex].defaultColor.b);
|
||||||
|
} else
|
||||||
|
if (LightAnimation.animationConfig.animation[i].stepType === "off") {
|
||||||
|
LEDstate[LightAnimation.lightsIndex].mode = "off";
|
||||||
|
strip.setPixel(LightAnimation.lightsIndex, 0, 0, 0);
|
||||||
|
} else print("*** animation stepType not implemented: ", LightAnimation.animationConfig[i].stepType);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do microanimation
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
animateBulb(i, LightAnimation.currentTick);
|
||||||
|
if (LightAnimation.currentTick >= LEDstate[i].nextTick) {
|
||||||
|
// update this animated bulb
|
||||||
|
}
|
||||||
|
// strip.setPixel called in animateBulb
|
||||||
|
// strip.setPixel(i, LightAnimation.adjustBrightness(LEDstate[i].currentRed), LightAnimation.adjustBrightness(LEDstate[i].currentGreen), LightAnimation.adjustBrightness(LEDstate[i].currentBlue));
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
} else print("*** Unknown LightAnimation.mode =", LightAnimation.mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Garbage collection before startup, full=true --> reclaim RAM to OS
|
||||||
|
gc(true);
|
||||||
|
|
||||||
|
print("Initialize timer, start in 5 seconds");
|
||||||
|
// Show start pattern for 5 seconds, then switch to default colors
|
||||||
|
Timer.set(5000 /* msec */, false /* repeat */, function() {
|
||||||
|
print("START animation");
|
||||||
|
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
||||||
|
strip.setPixel(i, LightAnimation.defaultColor.r, LightAnimation.defaultColor.g, LightAnimation.defaultColor.b);
|
||||||
|
}
|
||||||
|
strip.show();
|
||||||
|
Timer.set(LightAnimation.tickResolution, true /* repeat */, animateTick, null);
|
||||||
|
}, null);
|
|
@ -1,4 +1,4 @@
|
||||||
{
|
LightAnimation.animationConfig = {
|
||||||
"comment": "lights.cfg / JSON",
|
"comment": "lights.cfg / JSON",
|
||||||
"lights": [
|
"lights": [
|
||||||
{ "level":"EG", "room":"Wohnzimmer", "on":[200,200,200], "id":"EG-WoZi" },
|
{ "level":"EG", "room":"Wohnzimmer", "on":[200,200,200], "id":"EG-WoZi" },
|
||||||
|
@ -14,5 +14,17 @@
|
||||||
{ "level":"OG2", "room":"Buero 2.2", "on":[150,150,130], "id":"OG2-Buero-2" },
|
{ "level":"OG2", "room":"Buero 2.2", "on":[150,150,130], "id":"OG2-Buero-2" },
|
||||||
{ "level":"OG2", "room":"Buero 2.3", "on":[150,150,130], "id":"OG2-Buero-3" },
|
{ "level":"OG2", "room":"Buero 2.3", "on":[150,150,130], "id":"OG2-Buero-3" },
|
||||||
{ "level":"OG2", "room":"Buero 2.4", "on":[150,150,130], "id":"OG2-Buero-4" }
|
{ "level":"OG2", "room":"Buero 2.4", "on":[150,150,130], "id":"OG2-Buero-4" }
|
||||||
|
],
|
||||||
|
"animation": [
|
||||||
|
{ "lightsIndex": 2, "stepType": "on", "time": 1, "comment": "Fr. S. wacht auf" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "off", "time": 55, "comment": "Fr. S. geht ins Bad" },
|
||||||
|
{ "lightsIndex": 1, "stepType": "on", "time": 56, "comment": "Fr. S. duscht" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "on", "time": 250, "comment": "Fr. S. zieht sich an" },
|
||||||
|
{ "lightsIndex": 0, "stepType": "on", "time": 350, "comment": "Fr. S. setzt einen Kaffee auf und raeumt auf" },
|
||||||
|
{ "lightsIndex": 1, "stepType": "off", "time": 355, "comment": "Fr. S. ist fertig im Bad" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "off", "time": 400, "comment": "Fr. S. ist fertig im Schlafzimmer" },
|
||||||
|
{ "lightsIndex": 0, "stepType": "off", "time": 500, "comment": "Fr. S. hat gefruehstueckt und verlaesst die Wohnung" },
|
||||||
|
{ "lightsIndex": 3, "stepType": "on", "time": 520, "comment": "Fr. S. ist im Buero" },
|
||||||
|
{ "lightsIndex": 4, "stepType": "on", "time": 700, "comment": "Fr. S. ist im Buero" }
|
||||||
]
|
]
|
||||||
}
|
};
|
|
@ -0,0 +1,10 @@
|
||||||
|
/* api_ledlights.js */
|
||||||
|
|
||||||
|
/*
|
||||||
|
let LED = {
|
||||||
|
Red: ffi('int get_led_red(int)'),
|
||||||
|
Green: ffi('int get_led_red(int)'),
|
||||||
|
Blue: ffi('int get_led_red(int)'),
|
||||||
|
Tick: ffi('void led_evaluate_tick(void)')
|
||||||
|
};
|
||||||
|
*/
|
|
@ -0,0 +1,57 @@
|
||||||
|
load('api_config.js');
|
||||||
|
load('api_gpio.js');
|
||||||
|
load('api_mqtt.js');
|
||||||
|
load('api_net.js');
|
||||||
|
load('api_sys.js');
|
||||||
|
load('api_rpc.js');
|
||||||
|
load('api_timer.js');
|
||||||
|
/* load("animateLights.js"); */
|
||||||
|
load('api_ledlights.js');
|
||||||
|
|
||||||
|
let led = Cfg.get('pins.led');
|
||||||
|
|
||||||
|
// Helper C function get_led_gpio_pin() in src/main.c returns built-in LED GPIO
|
||||||
|
// let onBoardLed = ffi('int get_led_gpio_pin()')();
|
||||||
|
|
||||||
|
let button = Cfg.get('pins.button');
|
||||||
|
let topic = '/devices/' + Cfg.get('device.id') + '/events';
|
||||||
|
|
||||||
|
print('LED GPIO:', led, 'button GPIO:', button);
|
||||||
|
|
||||||
|
let getInfo = function() {
|
||||||
|
return JSON.stringify({
|
||||||
|
total_ram: Sys.total_ram(),
|
||||||
|
free_ram: Sys.free_ram()
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Blink built-in LED every second
|
||||||
|
GPIO.set_mode(led, GPIO.MODE_OUTPUT);
|
||||||
|
Timer.set(5000 /* 1 sec */, true /* repeat */, function() {
|
||||||
|
let value = GPIO.toggle(led);
|
||||||
|
print(value ? 'Tick' : 'Tock', 'uptime:', Sys.uptime(), getInfo());
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
// Publish to MQTT topic on a button press. Button is wired to GPIO pin 0
|
||||||
|
/*
|
||||||
|
GPIO.set_button_handler(button, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 200, function() {
|
||||||
|
let message = getInfo();
|
||||||
|
let ok = MQTT.pub(topic, message, 1);
|
||||||
|
print('Published:', ok, topic, '->', message);
|
||||||
|
}, null);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Monitor network connectivity.
|
||||||
|
Net.setStatusEventHandler(function(ev, arg) {
|
||||||
|
let evs = '???';
|
||||||
|
if (ev === Net.STATUS_DISCONNECTED) {
|
||||||
|
evs = 'DISCONNECTED';
|
||||||
|
} else if (ev === Net.STATUS_CONNECTING) {
|
||||||
|
evs = 'CONNECTING';
|
||||||
|
} else if (ev === Net.STATUS_CONNECTED) {
|
||||||
|
evs = 'CONNECTED';
|
||||||
|
} else if (ev === Net.STATUS_GOT_IP) {
|
||||||
|
evs = 'GOT_IP';
|
||||||
|
}
|
||||||
|
print('== Net event:', ev, evs);
|
||||||
|
}, null);
|
|
@ -0,0 +1,29 @@
|
||||||
|
{ "comment": "lights.cfg / JSON",
|
||||||
|
"lights": [
|
||||||
|
{ "level":"EG", "room":"Wohnzimmer", "on":[200,200,200], "id":"EG-WoZi" },
|
||||||
|
{ "level":"EG", "room":"Bad", "on":[150,150,150], "id":"EG-Bad" },
|
||||||
|
{ "level":"EG", "room":"Schlafzimmer", "on":[130,130,130], "id":"EG-Schlafen" },
|
||||||
|
{ "level":"EG", "room":"Buero 0.1", "on":[150,150,130], "id":"EG-Buero-1" },
|
||||||
|
{ "level":"EG", "room":"Buero 0.2", "on":[150,150,130], "id":"EG-Buero-2" },
|
||||||
|
{ "level":"OG1", "room":"Buero 1.1", "on":[150,150,130], "id":"OG1-Buero-1" },
|
||||||
|
{ "level":"OG1", "room":"Buero 1.2", "on":[150,150,130], "id":"OG1-Buero-2" },
|
||||||
|
{ "level":"OG1", "room":"Buero 1.3", "on":[150,150,130], "id":"OG1-Buero-3" },
|
||||||
|
{ "level":"OG1", "room":"Buero 1.4", "on":[150,150,130], "id":"OG1-Buero-4" },
|
||||||
|
{ "level":"OG2", "room":"Buero 2.1", "on":[150,150,130], "id":"OG2-Buero-1" },
|
||||||
|
{ "level":"OG2", "room":"Buero 2.2", "on":[150,150,130], "id":"OG2-Buero-2" },
|
||||||
|
{ "level":"OG2", "room":"Buero 2.3", "on":[150,150,130], "id":"OG2-Buero-3" },
|
||||||
|
{ "level":"OG2", "room":"Buero 2.4", "on":[150,150,130], "id":"OG2-Buero-4" }
|
||||||
|
],
|
||||||
|
"animation": [
|
||||||
|
{ "lightsIndex": 2, "stepType": "on", "time": 1, "comment": "Fr. S. wacht auf" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "off", "time": 55, "comment": "Fr. S. geht ins Bad" },
|
||||||
|
{ "lightsIndex": 1, "stepType": "on", "time": 56, "comment": "Fr. S. duscht" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "on", "time": 250, "comment": "Fr. S. zieht sich an" },
|
||||||
|
{ "lightsIndex": 0, "stepType": "on", "time": 350, "comment": "Fr. S. setzt einen Kaffee auf und raeumt auf" },
|
||||||
|
{ "lightsIndex": 1, "stepType": "off", "time": 355, "comment": "Fr. S. ist fertig im Bad" },
|
||||||
|
{ "lightsIndex": 2, "stepType": "off", "time": 400, "comment": "Fr. S. ist fertig im Schlafzimmer" },
|
||||||
|
{ "lightsIndex": 0, "stepType": "off", "time": 500, "comment": "Fr. S. hat gefruehstueckt und verlaesst die Wohnung" },
|
||||||
|
{ "lightsIndex": 3, "stepType": "on", "time": 520, "comment": "Fr. S. ist im Buero" },
|
||||||
|
{ "lightsIndex": 4, "stepType": "on", "time": 700, "comment": "Fr. S. ist im Buero" }
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
let x = {
|
||||||
|
"animation":
|
||||||
|
[
|
||||||
|
{"comment":"Frau Schlemmer wacht auf","time":1,"stepType":"on","lightsIndex":2},
|
||||||
|
{"comment":"Frau Schlemmer geht ins Bad","time":55,"stepType":"off","lightsIndex":2},
|
||||||
|
{"comment":"Frau Schlemmer duscht","time":56,"stepType":"on","lightsIndex":1},
|
||||||
|
{"comment":"Frau Schlemmer zieht sich an","time":250,"stepType":"on","lightsIndex":2},
|
||||||
|
{"comment":"Frau Schlemmer setzt einen Kaffee auf und r\xc3\x83\xc2\x83\xc3\x82\xc2\xa4umt auf","time":350,"stepType":"on","lightsIndex":0},
|
||||||
|
{"comment":"Frau Schlemmer ist fertig im Bad","time":355,"stepType":"off","lightsIndex":1},
|
||||||
|
{"comment":"Frau Schlemmer ist fertig im Schlafzimmer","time":400,"stepType":"off","lightsIndex":2},
|
||||||
|
{"comment":"Frau Schlemmer hat gefr\xc3\x83\xc2\x83\xc3\x82\xc2\xbchst\xc3\x83\xc2\x83\xc3\x82\xc2\xbcckt und verl\xc3\x83\xc2\x83\xc3\x82\xc2\xa4sst die Wohnung","time":500,"stepType":"off","lightsIndex":0},
|
||||||
|
{"comment":"Frau Schlemmer ist im B\xc3\x83\xc2\x83\xc3\x82\xc2\xbcro","time":520,"stepType":"on","lightsIndex":3},{"comment":"Frau Schlemmer ist im B\xc3\x83\xc2\x83\xc3\x82\xc2\xbcro","time":700,"stepType":"on","lightsIndex":4}
|
||||||
|
],
|
||||||
|
"lights":
|
||||||
|
[
|
||||||
|
{"id":"EG-WoZi","on":[200,200,200],"room":"Wohnzimmer","level":"EG"},
|
||||||
|
{"id":"EG-Bad","on":[150,150,150],"room":"Bad","level":"EG"},
|
||||||
|
{"id":"EG-Schlafen","on":[130,130,130],"room":"Schlafzimmer","level":"EG"},
|
||||||
|
{"id":"EG-Buero-1","on":[150,150,130],"room":"Buero 0.1","level":"EG"},
|
||||||
|
{"id":"EG-Buero-2","on":[150,150,130],"room":"Buero 0.2","level":"EG"},
|
||||||
|
{"id":"OG1-Buero-1","on":[150,150,130],"room":"Buero 1.1","level":"OG1"},
|
||||||
|
{"id":"OG1-Buero-2","on":[150,150,130],"room":"Buero 1.2","level":"OG1"},
|
||||||
|
{"id":"OG1-Buero-3","on":[150,150,130],"room":"Buero 1.3","level":"OG1"},
|
||||||
|
{"id":"OG1-Buero-4","on":[150,150,130],"room":"Buero 1.4","level":"OG1"},
|
||||||
|
{"id":"OG2-Buero-1","on":[150,150,130],"room":"Buero 2.1","level":"OG2"},
|
||||||
|
{"id":"OG2-Buero-2","on":[150,150,130],"room":"Buero 2.2","level":"OG2"},
|
||||||
|
{"id":"OG2-Buero-3","on":[150,150,130],"room":"Buero 2.3","level":"OG2"},
|
||||||
|
{"id":"OG2-Buero-4","on":[150,150,130],"room":"Buero 2.4","level":"OG2"}
|
||||||
|
],
|
||||||
|
"comment":"lights.cfg / JSON"
|
||||||
|
};
|
||||||
|
|
||||||
|
let LEDstate= [{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0},{"targetBlue":0,"targetGreen":0,"targetRed":0,"currentBlue":0,"currentGreen":0,"currentRed":0,"clockEndMinutes":59,"clockEndHours":23,"modeStep":0,"numLEDCtrl":0,"currentLEDCtrl":0,"mode":"onoff","rampTickDelta":0,"nextTick":0,"index":0}] ;
|
|
@ -1,162 +0,0 @@
|
||||||
load('api_rpc.js');
|
|
||||||
load("api_neopixel.js");
|
|
||||||
load("api_file.js");
|
|
||||||
|
|
||||||
|
|
||||||
let colors = {
|
|
||||||
black: {r:0, g:0, b:0},
|
|
||||||
red: {r:255, g:0, b:0},
|
|
||||||
green: {r:0, g:255, b:0},
|
|
||||||
blue: {r:0, g:0, b:255},
|
|
||||||
yellow: {r:255, g:255, b:0},
|
|
||||||
orange: {r:255, g:127, b:0},
|
|
||||||
purple: {r:255, g:0, b:255},
|
|
||||||
white: {r:255, g:255, b:255},
|
|
||||||
dimmedWhite: {r:50, g:50, b:50}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let LightAnimation = {
|
|
||||||
numberOfBulbs: 11,
|
|
||||||
ledPin: 2,
|
|
||||||
running: false,
|
|
||||||
mode: "demo",
|
|
||||||
generalBrightnessDivider: 3,
|
|
||||||
colorSequence: [
|
|
||||||
colors.dimmedWhite,
|
|
||||||
colors.red,
|
|
||||||
colors.green,
|
|
||||||
colors.blue,
|
|
||||||
colors.yellow,
|
|
||||||
colors.orange,
|
|
||||||
colors.purple,
|
|
||||||
colors.red,
|
|
||||||
colors.red,
|
|
||||||
colors.green,
|
|
||||||
colors.green,
|
|
||||||
colors.white,
|
|
||||||
colors.white
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add RPC handlers
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.Start', function(args) {
|
|
||||||
LightAnimation.running = true;
|
|
||||||
LightAnimation.mode = "run";
|
|
||||||
return {result: 'ok'};
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.Stop', function(args) {
|
|
||||||
LightAnimation.running = true; // first we want to turn off LEDs, thus needs stop-mode
|
|
||||||
LightAnimation.mode = "stop";
|
|
||||||
return {result: 'ok'};
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.DemoMode', function(args) {
|
|
||||||
LightAnimation.mode = "demo";
|
|
||||||
LightAnimation.running = true;
|
|
||||||
return {result: 'ok'};
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.CycleMode', function(args) {
|
|
||||||
LightAnimation.mode = "cycle";
|
|
||||||
LightAnimation.running = true;
|
|
||||||
return {result: 'ok'};
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.GetAnimationConfig', function(args) {
|
|
||||||
return LightAnimation.animationConfig;
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
RPC.addHandler('HouseLightning.SetAnimationConfig', function(args) {
|
|
||||||
if (args !== undefined && args.config !== undefined) {
|
|
||||||
LightAnimation.animationConfig = JSON.parse(args.config);
|
|
||||||
if (LightAnimation.animationConfig.comment === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / comment not found'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / lights definition not found'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[0] === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / lights not an array?'};
|
|
||||||
}
|
|
||||||
let i;
|
|
||||||
for (i=0; i<LightAnimation.animationConfig.lights.length; ++i) {
|
|
||||||
if (LightAnimation.animationConfig.lights[i].level === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / level missing in lights definition'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[i].room === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / room missing in lights definition'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[i].on === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / on missing in lights definition'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[i].on[0] === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / on not an array in lights definition?'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[i].on.length !== 3) {
|
|
||||||
return {error: 'config is incomplete or invalid / on must have 3 elements for R/G/B in lights definition?'};
|
|
||||||
}
|
|
||||||
if (LightAnimation.animationConfig.lights[i].id === undefined) {
|
|
||||||
return {error: 'config is incomplete or invalid / id missing in lights definition'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {result: 'ok'};
|
|
||||||
} else {
|
|
||||||
return {error: 'parameter config is required'};
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
function runNeopixel(strip, shiftBy) {
|
|
||||||
let i=0, colorIndex=0, color;
|
|
||||||
|
|
||||||
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
|
||||||
colorIndex = shiftBy % LightAnimation.colorSequence.length;
|
|
||||||
color = LightAnimation.colorSequence[colorIndex];
|
|
||||||
strip.setPixel(i, color.r/LightAnimation.generalBrightnessDivider, color.g/LightAnimation.generalBrightnessDivider, color.b/LightAnimation.generalBrightnessDivider);
|
|
||||||
// print("runNeo shift =", shiftBy, ", colorIndex =", colorIndex, ", r =", color.r, ", g =", color.g, ", b =", color.b);
|
|
||||||
shiftBy++;
|
|
||||||
}
|
|
||||||
strip.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
// initialize module
|
|
||||||
let configString = "";
|
|
||||||
configString = File.read("lights.cfg");
|
|
||||||
if (configString === "" || configString === null) configString = "***default***";
|
|
||||||
// print("configString = ", configString);
|
|
||||||
LightAnimation.animationConfig = JSON.parse(configString);
|
|
||||||
|
|
||||||
|
|
||||||
// initialize neoPixel
|
|
||||||
let colorOrder = NeoPixel.GRB;
|
|
||||||
let strip = NeoPixel.create(LightAnimation.ledPin, LightAnimation.numberOfBulbs, colorOrder);
|
|
||||||
strip.clear();
|
|
||||||
strip.show();
|
|
||||||
// initialize test pattern
|
|
||||||
let i = 0;
|
|
||||||
for (i=0; i<LightAnimation.numberOfBulbs; ++i) {
|
|
||||||
if (i % 2) {
|
|
||||||
strip.setPixel(i, 80, 20, 20);
|
|
||||||
} else {
|
|
||||||
strip.setPixel(i, 20, 80, 20);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
strip.show();
|
|
||||||
|
|
||||||
let shiftAnimation = 1;
|
|
||||||
|
|
||||||
Timer.set(1990 /* msec */, true /* repeat */, function() {
|
|
||||||
if (LightAnimation.running) {
|
|
||||||
if (LightAnimation.mode === "demo") {
|
|
||||||
runNeopixel(strip, shiftAnimation);
|
|
||||||
shiftAnimation++;
|
|
||||||
print("*** demo-mode, next shift=", shiftAnimation);
|
|
||||||
} else if (LightAnimation.mode === "stop") {
|
|
||||||
strip.clear();
|
|
||||||
strip.show();
|
|
||||||
LightAnimation.running = false;
|
|
||||||
} else print("*** no-ack, mode =", LightAnimation.mode);
|
|
||||||
} else print("*** stopped");
|
|
||||||
}, null);
|
|
67
fs/init.js
67
fs/init.js
|
@ -1,32 +1,65 @@
|
||||||
load('api_config.js');
|
load('api_config.js');
|
||||||
load('api_gpio.js');
|
load('api_gpio.js');
|
||||||
load('api_mqtt.js');
|
load('api_mqtt.js');
|
||||||
|
load('api_net.js');
|
||||||
load('api_sys.js');
|
load('api_sys.js');
|
||||||
load('api_rpc.js');
|
load('api_rpc.js');
|
||||||
load('api_timer.js');
|
load('api_timer.js');
|
||||||
load("animateLights.js");
|
|
||||||
|
let led = Cfg.get('pins.led');
|
||||||
|
|
||||||
// Helper C function get_led_gpio_pin() in src/main.c returns built-in LED GPIO
|
// Helper C function get_led_gpio_pin() in src/main.c returns built-in LED GPIO
|
||||||
let onBoardLed = ffi('int get_led_gpio_pin()')();
|
// let onBoardLed = ffi('int get_led_gpio_pin()')();
|
||||||
|
|
||||||
|
let button = Cfg.get('pins.button');
|
||||||
|
let topic = '/devices/' + Cfg.get('device.id') + '/events';
|
||||||
|
|
||||||
|
print('LED GPIO:', led, 'button GPIO:', button);
|
||||||
|
|
||||||
let getInfo = function() {
|
let getInfo = function() {
|
||||||
return JSON.stringify({total_ram: Sys.total_ram(), free_ram: Sys.free_ram()});
|
return JSON.stringify({
|
||||||
|
total_ram: Sys.total_ram(),
|
||||||
|
free_ram: Sys.free_ram()
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Publish to MQTT topic on a button press. Button is wired to GPIO pin 0
|
|
||||||
GPIO.set_button_handler(0, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 200, function() {
|
|
||||||
let topic = '/devices/' + Cfg.get('device.id') + '/events';
|
|
||||||
let message = getInfo();
|
|
||||||
let ok = MQTT.pub(topic, message, 1);
|
|
||||||
print('Published:', ok ? 'yes' : 'no', 'topic:', topic, 'message:', message);
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
|
|
||||||
// Blink built-in LED every second
|
// Blink built-in LED every second
|
||||||
GPIO.set_mode(onBoardLed, GPIO.MODE_OUTPUT);
|
GPIO.set_mode(led, GPIO.MODE_OUTPUT);
|
||||||
|
Timer.set(5000 /* 1 sec */, true /* repeat */, function() {
|
||||||
// Tick-Tock generation
|
let value = GPIO.toggle(led);
|
||||||
Timer.set(1000 /* 1 sec */, true /* repeat */, function() {
|
|
||||||
let value = GPIO.toggle(onBoardLed);
|
|
||||||
print(value ? 'Tick' : 'Tock', 'uptime:', Sys.uptime(), getInfo());
|
print(value ? 'Tick' : 'Tock', 'uptime:', Sys.uptime(), getInfo());
|
||||||
}, null);
|
}, null);
|
||||||
|
|
||||||
|
// Publish to MQTT topic on a button press. Button is wired to GPIO pin 0
|
||||||
|
/*
|
||||||
|
GPIO.set_button_handler(button, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 200, function() {
|
||||||
|
let message = getInfo();
|
||||||
|
let ok = MQTT.pub(topic, message, 1);
|
||||||
|
print('Published:', ok, topic, '->', message);
|
||||||
|
}, null);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Monitor network connectivity.
|
||||||
|
Net.setStatusEventHandler(function(ev, arg) {
|
||||||
|
let evs = '???';
|
||||||
|
if (ev === Net.STATUS_DISCONNECTED) {
|
||||||
|
evs = 'DISCONNECTED';
|
||||||
|
} else if (ev === Net.STATUS_CONNECTING) {
|
||||||
|
evs = 'CONNECTING';
|
||||||
|
} else if (ev === Net.STATUS_CONNECTED) {
|
||||||
|
evs = 'CONNECTED';
|
||||||
|
} else if (ev === Net.STATUS_GOT_IP) {
|
||||||
|
evs = 'GOT_IP';
|
||||||
|
}
|
||||||
|
print('== Net event:', ev, evs);
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
// Initialize LED controller
|
||||||
|
let addColor = ffi('void addColor(char *,int,int,int)');
|
||||||
|
let addLedDefinition = ffi('void addLedDefinition(char *, char *, char *, int, int, int)');
|
||||||
|
let addAnimationStep = ffi('void addAnimationStep(int, int, int)');
|
||||||
|
let LEDMode_on=1, LEDMode_off=2, LEDMode_blink=3, LEDMode_tv=4, LEDMode_fire=5;
|
||||||
|
let LEDStateEngine_start = ffi('void startLEDStateEngine(void)');
|
||||||
|
let LEDStateEngine_pause = ffi('void pauseLEDStateEngine(void)');
|
||||||
|
|
||||||
|
|
||||||
|
|
5
mos.yml
5
mos.yml
|
@ -29,7 +29,10 @@ libs:
|
||||||
- origin: https://github.com/mongoose-os-libs/aws
|
- origin: https://github.com/mongoose-os-libs/aws
|
||||||
- origin: https://github.com/mongoose-os-libs/mjs
|
- origin: https://github.com/mongoose-os-libs/mjs
|
||||||
config_schema:
|
config_schema:
|
||||||
- ["i2c.enable", true]
|
- ["i2c.enable", false]
|
||||||
|
- ["led.pin", "i", 2, {title: "Pin for data to WS2812 based LED chain"}]
|
||||||
|
- ["led.useDefaults", "b", true, {title: "Use default values for colors, led-definitions, animations"}]
|
||||||
|
- ["led.count", "i", 5, {title: "Number of LEDs connected in the chain"}]
|
||||||
build_vars:
|
build_vars:
|
||||||
MGOS_ENABLE_ONEWIRE: 1
|
MGOS_ENABLE_ONEWIRE: 1
|
||||||
cflags: []
|
cflags: []
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/* AnimationConfig.cpp */
|
||||||
|
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
#include "AnimationConfig.h"
|
||||||
|
|
||||||
|
AnimationConfig theAnimationConfig;
|
||||||
|
|
||||||
|
uint16_t getNumberOfAnimationSteps() { return theAnimationConfig.numberAnimationSteps; };
|
||||||
|
|
||||||
|
static uint8_t verifyAnimationStepIndex(uint8_t index) {
|
||||||
|
if (/*index < 0 ||*/ index >= MAX_ANIMATION_STEPS) {
|
||||||
|
LOG(LL_ERROR, ("Invalid animation step index %d, set to 0", index));
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AnimationStep_init(uint16_t animationStepIndex, uint8_t ledIndex, LEDMode m, uint32_t duration) {
|
||||||
|
animationStepIndex = verifyAnimationStepIndex(animationStepIndex);
|
||||||
|
theAnimationConfig.animationStep[animationStepIndex].ledIndex = ledIndex;
|
||||||
|
theAnimationConfig.animationStep[animationStepIndex].ledMode = m;
|
||||||
|
theAnimationConfig.animationStep[animationStepIndex].durationTicks = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationStep_add(uint8_t ledIndex, LEDMode m, uint32_t duration) {
|
||||||
|
uint16_t index = theAnimationConfig.numberAnimationSteps;
|
||||||
|
if (index >= MAX_ANIMATION_STEPS) {
|
||||||
|
LOG(LL_ERROR, ("AnimationStep_add: too many animation steps! ledIndex=%d", ledIndex));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
theAnimationConfig.numberAnimationSteps++;
|
||||||
|
theAnimationConfig.animationStep[index].ledIndex = ledIndex;
|
||||||
|
theAnimationConfig.animationStep[index].ledMode = m;
|
||||||
|
theAnimationConfig.animationStep[index].durationTicks = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AnimationConfig_init(bool loadDefaultData) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0; i<MAX_ANIMATION_STEPS; ++i) {
|
||||||
|
AnimationStep_init(i, -1, LEDMode_off, 0);
|
||||||
|
}
|
||||||
|
theAnimationConfig.numberAnimationSteps = 0;
|
||||||
|
|
||||||
|
if (loadDefaultData) {
|
||||||
|
AnimationStep_add(0, LEDMode_on, 30);
|
||||||
|
AnimationStep_add(0, LEDMode_off, 30);
|
||||||
|
AnimationStep_add(0, LEDMode_on, 10);
|
||||||
|
AnimationStep_add(0, LEDMode_off, 20);
|
||||||
|
AnimationStep_add(0, LEDMode_on, 50);
|
||||||
|
AnimationStep_add(0, LEDMode_off, 10);
|
||||||
|
AnimationStep_add(0, LEDMode_on, 5);
|
||||||
|
AnimationStep_add(0, LEDMode_off, 5);
|
||||||
|
AnimationStep_add(1, LEDMode_on, 20);
|
||||||
|
AnimationStep_add(1, LEDMode_off, 60);
|
||||||
|
AnimationStep_add(1, LEDMode_on, 10);
|
||||||
|
AnimationStep_add(1, LEDMode_off, 70);
|
||||||
|
AnimationStep_add(2, LEDMode_off, 20);
|
||||||
|
AnimationStep_add(2, LEDMode_on, 80);
|
||||||
|
AnimationStep_add(2, LEDMode_off, 5);
|
||||||
|
AnimationStep_add(2, LEDMode_on, 60);
|
||||||
|
AnimationStep_add(2, LEDMode_off, 10);
|
||||||
|
AnimationStep_add(3, LEDMode_on, 11);
|
||||||
|
AnimationStep_add(3, LEDMode_off, 11);
|
||||||
|
AnimationStep_add(4, LEDMode_on, 12);
|
||||||
|
AnimationStep_add(4, LEDMode_off, 12);
|
||||||
|
AnimationStep_add(4, LEDMode_on, 13);
|
||||||
|
AnimationStep_add(4, LEDMode_off, 13);
|
||||||
|
|
||||||
|
for (i=5; i<LEDStateEngine_getNumberOfLeds(); ++i) {
|
||||||
|
AnimationStep_add(i, LEDMode_on, 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t AnimationConfig_getNextAnimationStepForLED(uint8_t led, uint16_t currentAnimationStep) {
|
||||||
|
for (uint16_t i=currentAnimationStep+1; i<MAX_ANIMATION_STEPS; ++i) {
|
||||||
|
if (theAnimationConfig.animationStep[i].ledIndex == led) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// nothing found, thus start from beginning again
|
||||||
|
for (uint16_t i=0; i<=currentAnimationStep; ++i) {
|
||||||
|
if (theAnimationConfig.animationStep[i].ledIndex == led) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ERROR, should never reach this code
|
||||||
|
LOG(LL_ERROR, ("getNextAnimationStepForLED -- could not find entry for led %d, current step=%d", led, currentAnimationStep));
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* AnimationConfig.h */
|
||||||
|
|
||||||
|
#ifndef AnimationConfig_loaded
|
||||||
|
#define AnimationConfig_loaded
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "fw/src/mgos.h"
|
||||||
|
#include "common/cs_dbg.h"
|
||||||
|
#include "mjs.h"
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
#include "LEDState.h"
|
||||||
|
|
||||||
|
#define MAX_ANIMATION_STEPS 100
|
||||||
|
|
||||||
|
typedef struct AnimationStep_t {
|
||||||
|
uint8_t ledIndex;
|
||||||
|
LEDMode ledMode;
|
||||||
|
uint32_t durationTicks;
|
||||||
|
} AnimationStep;
|
||||||
|
|
||||||
|
typedef struct AnimationConfig_t {
|
||||||
|
uint16_t numberAnimationSteps;
|
||||||
|
AnimationStep animationStep[MAX_ANIMATION_STEPS];
|
||||||
|
} AnimationConfig;
|
||||||
|
|
||||||
|
extern AnimationConfig theAnimationConfig;
|
||||||
|
|
||||||
|
extern void AnimationConfig_init(bool loadDefaultData);
|
||||||
|
extern uint16_t getNumberOfAnimationSteps();
|
||||||
|
extern void AnimationStep_add(uint8_t ledIndex, LEDMode m, uint32_t duration);
|
||||||
|
extern uint16_t AnimationConfig_getNextAnimationStepForLED(uint8_t led, uint16_t currentAnimationStep);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,97 @@
|
||||||
|
/* LEDDefinition.cpp */
|
||||||
|
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
|
||||||
|
LEDColor offColor = { name: "off", red: 0, green: 0, blue: 0};
|
||||||
|
LEDColor dimmedRedColor = { name: "dimmedRed", red: 80, green: 0, blue: 0};
|
||||||
|
|
||||||
|
#define MAX_COLORS 20
|
||||||
|
static LEDColor ledColor[MAX_COLORS];
|
||||||
|
static int numberOfColors = 0;
|
||||||
|
|
||||||
|
LEDColor *LEDColor_get(char *name) {
|
||||||
|
for (int i=0; i<numberOfColors; ++i) {
|
||||||
|
if (strcmp(name, ledColor[i].name) == 0)
|
||||||
|
return &ledColor[i];
|
||||||
|
}
|
||||||
|
LOG(LL_ERROR, ("Error: Color not found: %s", name));
|
||||||
|
return &offColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDColor_add(char *name, uint8_t red, uint8_t green, uint8_t blue) {
|
||||||
|
if (numberOfColors < MAX_COLORS) {
|
||||||
|
ledColor[numberOfColors].name = name;
|
||||||
|
ledColor[numberOfColors].red = red;
|
||||||
|
ledColor[numberOfColors].green = green;
|
||||||
|
ledColor[numberOfColors].blue = blue;
|
||||||
|
++numberOfColors;
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("Too many color definitions, igrnoring %s (%d, %d, %d)", name, red, green, blue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDColor_init(bool loadDefaultData) {
|
||||||
|
if (loadDefaultData) {
|
||||||
|
LEDColor_add("off", 0, 0, 0);
|
||||||
|
LEDColor_add("red", 255, 0, 0);
|
||||||
|
LEDColor_add("green", 0, 255, 0);
|
||||||
|
LEDColor_add("blue", 0, 0, 255);
|
||||||
|
LEDColor_add("brightWhite", 255, 255, 255);
|
||||||
|
LEDColor_add("white", 180, 180, 180);
|
||||||
|
LEDColor_add("dimmedWhite", 100, 100, 100);
|
||||||
|
LEDColor_add("dimmedRed", 100, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LEDDefinition ledDefinition[MAX_LEDS];
|
||||||
|
static int numberOfLEDDefinitions = 0;
|
||||||
|
|
||||||
|
static int verifyLedDefinitionNum(int ledNum) {
|
||||||
|
if (ledNum < 0) {
|
||||||
|
LOG(LL_ERROR, ("Error: verifyLedDefinitionNum failed for ledNum=%d", ledNum));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return ledNum % numberOfLEDDefinitions;
|
||||||
|
}
|
||||||
|
|
||||||
|
LEDDefinition *LEDDefinition_get(int ledNum) {
|
||||||
|
ledNum = verifyLedDefinitionNum(ledNum);
|
||||||
|
return &ledDefinition[ledNum];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDDefinition_add(char *level, char *room, char *id, uint8_t red, uint8_t green, uint8_t blue) {
|
||||||
|
if (numberOfLEDDefinitions < MAX_LEDS) {
|
||||||
|
ledDefinition[numberOfLEDDefinitions].level = level;
|
||||||
|
ledDefinition[numberOfLEDDefinitions].room = room;
|
||||||
|
ledDefinition[numberOfLEDDefinitions].id = id;
|
||||||
|
ledDefinition[numberOfLEDDefinitions].onColor.red = red;
|
||||||
|
ledDefinition[numberOfLEDDefinitions].onColor.green = green;
|
||||||
|
ledDefinition[numberOfLEDDefinitions].onColor.blue = blue;
|
||||||
|
++numberOfLEDDefinitions;
|
||||||
|
} else {
|
||||||
|
LOG(LL_ERROR, ("Error: Too many LED Definitions to load, ignoring level=%s, room=%s, id=%s", level, room, id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void LEDDefinition_init(int numberOfLeds, bool loadDefaultData) {
|
||||||
|
// initialize
|
||||||
|
numberOfLEDDefinitions = 0;
|
||||||
|
if (loadDefaultData) {
|
||||||
|
do {
|
||||||
|
LEDDefinition_add("EG", "Wohnzimmer", "EG-WoZi", 200,200,200);
|
||||||
|
LEDDefinition_add("EG", "Bad", "EG-Bad", 130,130,130);
|
||||||
|
LEDDefinition_add("EG", "Schlafzimmer", "EG-Schlafen", 150,150,130);
|
||||||
|
LEDDefinition_add("EG", "Buero 0.1", "EG-Buero-1", 150,130,130);
|
||||||
|
LEDDefinition_add("EG", "Buero 0.2", "EG-Buero-2", 150,200,200);
|
||||||
|
LEDDefinition_add("EG", "Flur", "EG-Flur-1", 100,100,100);
|
||||||
|
LEDDefinition_add("EG", "Flur", "EG-Flur-2", 140,100,100);
|
||||||
|
LEDDefinition_add("OG1", "Buero 1.1", "OG1-WoZi", 200,200,200);
|
||||||
|
LEDDefinition_add("OG1", "Bad OG1", "OG1-Bad", 130,130,130);
|
||||||
|
LEDDefinition_add("OG1", "Buero 1.2", "OG1-Schlafen", 150,150,130);
|
||||||
|
LEDDefinition_add("OG1", "Buero 1.3", "OG1Buero-1", 150,150,130);
|
||||||
|
LEDDefinition_add("OG1", "Buero 1.4", "OG1-Buero-2", 150,200,200);
|
||||||
|
LEDDefinition_add("OG1", "Flur", "OG1-Flur-1", 100,100,100);
|
||||||
|
LEDDefinition_add("OG1", "Flur", "OG1-Flur-2", 140,100,100);
|
||||||
|
} while(numberOfLEDDefinitions < numberOfLeds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/* LEDDefinition.h */
|
||||||
|
|
||||||
|
#ifndef LEDDefinition_included
|
||||||
|
#define LEDDefinition_included
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "fw/src/mgos.h"
|
||||||
|
#include "mjs.h"
|
||||||
|
#include "mongoose/mongoose.h"
|
||||||
|
#include "common/cs_dbg.h"
|
||||||
|
|
||||||
|
#define MAX_LEDS 100
|
||||||
|
|
||||||
|
typedef enum LEDMode_e { LEDMode_on, LEDMode_off, LEDMode_blink, LEDMode_tv, LEDMode_fire } LEDMode;
|
||||||
|
|
||||||
|
typedef struct LEDColor_t {
|
||||||
|
char *name;
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
} LEDColor;
|
||||||
|
|
||||||
|
extern LEDColor offColor, dimmedRedColor;
|
||||||
|
extern void LEDColor_init(bool loadDefaultData);
|
||||||
|
extern LEDColor *LEDColor_get(char *name);
|
||||||
|
extern void LEDColor_add(char *name, uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
|
||||||
|
typedef struct LEDDefinition_t {
|
||||||
|
char *level;
|
||||||
|
char *room;
|
||||||
|
char *id;
|
||||||
|
LEDColor onColor;
|
||||||
|
} LEDDefinition;
|
||||||
|
|
||||||
|
extern LEDDefinition ledDefinition[MAX_LEDS];
|
||||||
|
|
||||||
|
extern LEDDefinition *LEDDefinition_get(int ledNum);
|
||||||
|
extern void LEDDefinition_init(int numberOfLeds, bool loadDefaultData);
|
||||||
|
void LEDDefinition_add(char *level, char *room, char *id, uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,113 @@
|
||||||
|
/* LEDState.c */
|
||||||
|
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
#include "LEDState.h"
|
||||||
|
#include "AnimationConfig.h"
|
||||||
|
|
||||||
|
|
||||||
|
static LEDStateEngine theLEDStateEngine;
|
||||||
|
|
||||||
|
void LEDStateEngine_setNumberOfLeds(int numberOfLeds) {
|
||||||
|
if (numberOfLeds < 1 || numberOfLeds > MAX_LEDS) {
|
||||||
|
LOG(LL_ERROR, ("Invalid number of LEDs %d", numberOfLeds));
|
||||||
|
numberOfLeds = MAX_LEDS;
|
||||||
|
}
|
||||||
|
theLEDStateEngine.numberOfLeds = numberOfLeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDStateEngine_init(int numberOfLeds) {
|
||||||
|
LEDStateEngine_setNumberOfLeds(numberOfLeds);
|
||||||
|
theLEDStateEngine.comment = "";
|
||||||
|
for (int i=0; i<numberOfLeds; ++i) {
|
||||||
|
LEDstate_init(i, LEDDefinition_get(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDStateEngine_addComment(char *comment) {
|
||||||
|
theLEDStateEngine.comment = comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t LEDStateEngine_getNumberOfLeds() {
|
||||||
|
return theLEDStateEngine.numberOfLeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void LEDStateEngine_tick() {
|
||||||
|
for (int i=0; i<theLEDStateEngine.numberOfLeds; ++i) {
|
||||||
|
LEDState_tick(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDState_tick(int ledNum) {
|
||||||
|
LEDState *state = &theLEDStateEngine.ledState[ledNum];
|
||||||
|
if (/*state->index < 0 ||*/ state->index >= theLEDStateEngine.numberOfLeds)
|
||||||
|
return; // ignore LED entries, that are not setup correctly
|
||||||
|
state->currentTick++;
|
||||||
|
if (state->currentTick == state->nextTick) {
|
||||||
|
// perform/initiate next state change
|
||||||
|
state->currentAnimationStep = AnimationConfig_getNextAnimationStepForLED(ledNum, state->currentAnimationStep);
|
||||||
|
state->nextTick = state->currentTick + theAnimationConfig.animationStep[state->currentAnimationStep].durationTicks;
|
||||||
|
state->mode = theAnimationConfig.animationStep[state->currentAnimationStep].ledMode;
|
||||||
|
switch (state->mode) {
|
||||||
|
case LEDMode_on:
|
||||||
|
state->currentColor = state->onColor;
|
||||||
|
break;
|
||||||
|
case LEDMode_off:
|
||||||
|
state->currentColor = offColor;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state->currentColor = dimmedRedColor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// do animations, if necessary
|
||||||
|
}
|
||||||
|
|
||||||
|
LEDState *LEDStateEngine_getLedState(int n) {
|
||||||
|
if (n>=0 && n<theLEDStateEngine.numberOfLeds)
|
||||||
|
return &theLEDStateEngine.ledState[n];
|
||||||
|
LOG(LL_ERROR, ("*** LEDStateEngine_getLedState -- invalid LED number given: %d", n));
|
||||||
|
return &theLEDStateEngine.ledState[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LEDState */
|
||||||
|
static int verifyLedNumber(int n) {
|
||||||
|
if (n < 0 || n >= theLEDStateEngine.numberOfLeds) {
|
||||||
|
LOG(LL_ERROR, ("*** Invalid LED number %d, set to zero", n));
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDstate_initDefault(int n, LEDMode defaultMode) {
|
||||||
|
n = verifyLedNumber(n);
|
||||||
|
LEDState *state = &theLEDStateEngine.ledState[n];
|
||||||
|
state->index = n;
|
||||||
|
state->mode = defaultMode;
|
||||||
|
state->nextTick = 0;
|
||||||
|
state->ledDefinition = LEDDefinition_get(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LEDstate_init(int n, LEDDefinition *ledDefinition) {
|
||||||
|
n = verifyLedNumber(n);
|
||||||
|
LEDstate_initDefault(n, LEDMode_off);
|
||||||
|
LEDState *state = LEDStateEngine_getLedState(n);
|
||||||
|
state->index = n;
|
||||||
|
state->ledDefinition = ledDefinition;
|
||||||
|
state->onColor = ledDefinition->onColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int LEDState_getLedRed(int ledNum) {
|
||||||
|
LEDState *state = LEDStateEngine_getLedState(ledNum);
|
||||||
|
return state->currentColor.red;
|
||||||
|
}
|
||||||
|
int LEDState_getLedGreen(int ledNum) {
|
||||||
|
LEDState *state = LEDStateEngine_getLedState(ledNum);
|
||||||
|
return state->currentColor.green;
|
||||||
|
}
|
||||||
|
int LEDState_getLedBlue(int ledNum) {
|
||||||
|
LEDState *state = LEDStateEngine_getLedState(ledNum);
|
||||||
|
return state->currentColor.blue;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* LEDState.h */
|
||||||
|
|
||||||
|
#ifndef LEDState_loaded
|
||||||
|
#define LEDState_loaded
|
||||||
|
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
|
||||||
|
typedef struct LEDState_t {
|
||||||
|
uint8_t index;
|
||||||
|
LEDDefinition *ledDefinition;
|
||||||
|
uint32_t currentTick;
|
||||||
|
uint32_t nextTick;
|
||||||
|
uint8_t rampTickDelta;
|
||||||
|
LEDMode mode;
|
||||||
|
uint8_t currentLEDCtrlIndex; // ???????????
|
||||||
|
uint16_t currentAnimationStep;
|
||||||
|
uint8_t modeStep; // sub-state within a mode
|
||||||
|
uint8_t clockEndHours;
|
||||||
|
uint8_t clockEndMinutes;
|
||||||
|
LEDColor currentColor;
|
||||||
|
LEDColor onColor; // for target of on-mode
|
||||||
|
LEDColor targetColor;
|
||||||
|
} LEDState;
|
||||||
|
|
||||||
|
typedef struct LEDStateEngine_t {
|
||||||
|
uint8_t numberOfLeds;
|
||||||
|
LEDState ledState[MAX_LEDS];
|
||||||
|
char *comment;
|
||||||
|
} LEDStateEngine;
|
||||||
|
|
||||||
|
/* LEDStateEngine */
|
||||||
|
extern void LEDStateEngine_init(int numberOfLeds);
|
||||||
|
extern void LEDStateEngine_addComment(char *comment);
|
||||||
|
|
||||||
|
extern LEDState *LEDStateEngine_getLedState(int n);
|
||||||
|
extern void LEDStateEngine_tick();
|
||||||
|
extern uint16_t LEDStateEngine_getNumberOfLeds();
|
||||||
|
|
||||||
|
/* LEDState */
|
||||||
|
extern void LEDstate_init(int n, LEDDefinition *ledDefinition);
|
||||||
|
extern void LEDstate_initDefault(int n, LEDMode defaultMode);
|
||||||
|
extern void LEDState_tick(int ledNum);
|
||||||
|
extern int LEDState_getLedRed(int ledNum);
|
||||||
|
extern int LEDState_getLedGreen(int ledNum);
|
||||||
|
extern int LEDState_getLedBlue(int ledNum);
|
||||||
|
|
||||||
|
#endif
|
71
src/main.c
71
src/main.c
|
@ -2,13 +2,17 @@
|
||||||
|
|
||||||
#include "common/platform.h"
|
#include "common/platform.h"
|
||||||
#include "common/cs_file.h"
|
#include "common/cs_file.h"
|
||||||
#include "fw/src/mgos_app.h"
|
#include "mgos_app.h"
|
||||||
#include "fw/src/mgos_gpio.h"
|
#include "mgos_gpio.h"
|
||||||
#include "fw/src/mgos_sys_config.h"
|
#include "mgos_sys_config.h"
|
||||||
#include "fw/src/mgos_timers.h"
|
#include "mgos_system.h"
|
||||||
|
#include "mgos_timers.h"
|
||||||
#include "fw/src/mgos_hal.h"
|
#include "fw/src/mgos_hal.h"
|
||||||
#include "fw/src/mgos_dlsym.h"
|
#include "fw/src/mgos_dlsym.h"
|
||||||
#include "mjs.h"
|
#include "mjs.h"
|
||||||
|
#include "LEDDefinition.h"
|
||||||
|
#include "AnimationConfig.h"
|
||||||
|
#include "LEDState.h"
|
||||||
|
|
||||||
#if CS_PLATFORM == CS_P_ESP8266
|
#if CS_PLATFORM == CS_P_ESP8266
|
||||||
#define LED_GPIO 2 /* On ESP-12E there is a blue LED connected to GPIO2 */
|
#define LED_GPIO 2 /* On ESP-12E there is a blue LED connected to GPIO2 */
|
||||||
|
@ -27,9 +31,64 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int get_led_gpio_pin(void) {
|
int get_led_gpio_pin(void) {
|
||||||
return LED_GPIO;
|
return LED_GPIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
LEDStateEngine ledStateEngine;
|
||||||
|
static bool stateEngineRunning = false;
|
||||||
|
|
||||||
|
static void timer_cb() {
|
||||||
|
if (stateEngineRunning) {
|
||||||
|
LOG(LL_INFO, ("tick"));
|
||||||
|
LEDStateEngine_tick();
|
||||||
|
} else {
|
||||||
|
LOG(LL_INFO, ("--paused--"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void delayed_boot() {
|
||||||
|
mgos_set_timer(100, true, timer_cb, NULL); // every 0.1 second call timer_cb
|
||||||
}
|
}
|
||||||
|
|
||||||
enum mgos_app_init_result mgos_app_init(void) {
|
enum mgos_app_init_result mgos_app_init(void) {
|
||||||
return MGOS_APP_INIT_SUCCESS;
|
int numberOfLeds = 20; /*mgos_sys_config_get_led_count()*/
|
||||||
|
bool loadDefaultData = true; /*mgos_sys_config_get_led_useDefaults()*/
|
||||||
|
|
||||||
|
LEDColor_init(loadDefaultData);
|
||||||
|
LEDDefinition_init(numberOfLeds, loadDefaultData);
|
||||||
|
AnimationConfig_init(loadDefaultData);
|
||||||
|
LEDStateEngine_init(numberOfLeds);
|
||||||
|
if (loadDefaultData) {
|
||||||
|
stateEngineRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgos_set_timer(5000, false, delayed_boot, NULL); // after 5 seconds boot the functions
|
||||||
|
return MGOS_APP_INIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// javascript adapter functions:
|
||||||
|
void addColor(char *name, int red, int green, int blue) {
|
||||||
|
LEDColor_add(name, red, green, blue);
|
||||||
|
}
|
||||||
|
void addLedDefinition(char *level, char *room, char *id, int red, int green, int blue) {
|
||||||
|
LEDDefinition_add(level, room, id, red, green, blue);
|
||||||
|
}
|
||||||
|
void addAnimationStep(int ledIndex, int ledMode, int duration) {
|
||||||
|
LEDMode m = LEDMode_off;
|
||||||
|
switch (ledMode) {
|
||||||
|
case 1: m = LEDMode_on; break;
|
||||||
|
case 2: m = LEDMode_off; break;
|
||||||
|
case 3: m = LEDMode_blink; break;
|
||||||
|
case 4: m = LEDMode_tv; break;
|
||||||
|
case 5: m = LEDMode_fire; break;
|
||||||
|
default: m = LEDMode_off;
|
||||||
|
}
|
||||||
|
AnimationStep_add(ledIndex, m, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startLEDStateEngine(void) {
|
||||||
|
stateEngineRunning = true;
|
||||||
|
}
|
||||||
|
void pauseLEDStateEngine(void) {
|
||||||
|
stateEngineRunning = false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue