From e2209504dae006f34dd464a80d260cd77a13b69e Mon Sep 17 00:00:00 2001 From: Dirk Jahnke Date: Sat, 25 Nov 2017 23:58:24 +0100 Subject: [PATCH] 1st version with c implementation of state engine --- bak/animateLights-bak.js | 352 ++++++++++++++++++++++ bak/animateLights.js | 371 ++++++++++++++++++++++++ fs/lights.cfg => bak/animationConfig.js | 16 +- bak/api_ledlights.js | 10 + bak/init-bak.js | 57 ++++ bak/lights.cfg | 29 ++ bak/test.js | 33 +++ fs/animateLights.js | 162 ----------- fs/init.js | 67 +++-- mos.yml | 5 +- src/AnimationConfig.c | 93 ++++++ src/AnimationConfig.h | 33 +++ src/LEDDefinition.c | 97 +++++++ src/LEDDefinition.h | 41 +++ src/LEDState.c | 113 ++++++++ src/LEDState.h | 47 +++ src/main.c | 71 ++++- 17 files changed, 1409 insertions(+), 188 deletions(-) create mode 100644 bak/animateLights-bak.js create mode 100755 bak/animateLights.js rename fs/lights.cfg => bak/animationConfig.js (51%) mode change 100755 => 100644 create mode 100644 bak/api_ledlights.js create mode 100644 bak/init-bak.js create mode 100755 bak/lights.cfg create mode 100644 bak/test.js delete mode 100755 fs/animateLights.js create mode 100644 src/AnimationConfig.c create mode 100644 src/AnimationConfig.h create mode 100644 src/LEDDefinition.c create mode 100644 src/LEDDefinition.h create mode 100644 src/LEDState.c create mode 100644 src/LEDState.h diff --git a/bak/animateLights-bak.js b/bak/animateLights-bak.js new file mode 100644 index 0000000..461735d --- /dev/null +++ b/bak/animateLights-bak.js @@ -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= 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= 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', 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); diff --git a/bak/lights.cfg b/bak/lights.cfg new file mode 100755 index 0000000..e958b80 --- /dev/null +++ b/bak/lights.cfg @@ -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" } + ] +} \ No newline at end of file diff --git a/bak/test.js b/bak/test.js new file mode 100644 index 0000000..7ea97b0 --- /dev/null +++ b/bak/test.js @@ -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}] ; diff --git a/fs/animateLights.js b/fs/animateLights.js deleted file mode 100755 index c62c6e7..0000000 --- a/fs/animateLights.js +++ /dev/null @@ -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', 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)'); + + diff --git a/mos.yml b/mos.yml index 4879d33..9a57ac9 100644 --- a/mos.yml +++ b/mos.yml @@ -29,7 +29,10 @@ libs: - origin: https://github.com/mongoose-os-libs/aws - origin: https://github.com/mongoose-os-libs/mjs 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: MGOS_ENABLE_ONEWIRE: 1 cflags: [] diff --git a/src/AnimationConfig.c b/src/AnimationConfig.c new file mode 100644 index 0000000..9bc1158 --- /dev/null +++ b/src/AnimationConfig.c @@ -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 +#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 \ No newline at end of file diff --git a/src/LEDDefinition.c b/src/LEDDefinition.c new file mode 100644 index 0000000..92bb671 --- /dev/null +++ b/src/LEDDefinition.c @@ -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 +#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 \ No newline at end of file diff --git a/src/LEDState.c b/src/LEDState.c new file mode 100644 index 0000000..f8b6d40 --- /dev/null +++ b/src/LEDState.c @@ -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; iindex < 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) { + 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; +} + diff --git a/src/LEDState.h b/src/LEDState.h new file mode 100644 index 0000000..1319374 --- /dev/null +++ b/src/LEDState.h @@ -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 diff --git a/src/main.c b/src/main.c index 98ef83f..aede589 100644 --- a/src/main.c +++ b/src/main.c @@ -2,13 +2,17 @@ #include "common/platform.h" #include "common/cs_file.h" -#include "fw/src/mgos_app.h" -#include "fw/src/mgos_gpio.h" -#include "fw/src/mgos_sys_config.h" -#include "fw/src/mgos_timers.h" +#include "mgos_app.h" +#include "mgos_gpio.h" +#include "mgos_sys_config.h" +#include "mgos_system.h" +#include "mgos_timers.h" #include "fw/src/mgos_hal.h" #include "fw/src/mgos_dlsym.h" #include "mjs.h" +#include "LEDDefinition.h" +#include "AnimationConfig.h" +#include "LEDState.h" #if CS_PLATFORM == CS_P_ESP8266 #define LED_GPIO 2 /* On ESP-12E there is a blue LED connected to GPIO2 */ @@ -27,9 +31,64 @@ #endif 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) { - 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; }