Added new mode: Scheduler -- clock based switching of lights

This commit is contained in:
Dirk Jahnke 2017-12-09 22:10:59 +01:00
parent 626fe42a90
commit 4aeebe1b33
12 changed files with 290 additions and 50 deletions

View File

@ -1,28 +1,39 @@
let addColor = ffi('void addColor(char *,int,int,int)');
let LEDDefinition_addByName = ffi('void LEDDefinition_addByName(char *, char *, char *, char *)');
let addAnimationStep = ffi('void addAnimationStep(int, char *, int)');
let LEDMode_on=1, LEDMode_off=2, LEDMode_blink=3, LEDMode_tv=4, LEDMode_fire=5;
let LEDStateEngine_init = ffi('void LEDStateEngine_init(int, int)');
let LEDStateEngine_start = ffi('void startLEDStateEngine(void)');
let LEDStateEngine_pause = ffi('void pauseLEDStateEngine(void)');
let LEDStateEngine_getMinTickTime = ffi('double LEDStateEngine_getMinTickTime(void)');
let LEDStateEngine_getMaxTickTime = ffi('double LEDStateEngine_getMaxTickTime(void)');
let LEDState_getRed = ffi('int LEDState_getLedRed(int)');
let LEDState_getGreen = ffi('int LEDState_getLedGreen(int)');
let LEDState_getBlue = ffi('int LEDState_getLedBlue(int)');
let LEDState_getColorName = ffi('char * LEDState_getLedColorName(int)');
let LEDState_getNextTick = ffi('int LEDState_getNextTick(int)');
let LEDState_getCurrentTick = ffi('int LEDState_getCurrentTick(int)');
let LEDDefinition_addByName = ffi('void LEDDefinition_addByName(char *, char *, char *, char *)');
let LEDDefinition_getLevel = ffi('char *LEDDefinition_getLevel(int)');
let LEDDefinition_getRoom = ffi('char *LEDDefinition_getRoom(int)');
let LEDDefinition_getId = ffi('char *LEDDefinition_getId(int)');
let LEDDefinition_getOnColorRed = ffi('int LEDDefinition_getOnColorRed(int)');
let LEDDefinition_getOnColorGreen = ffi('int LEDDefinition_getOnColorGreen(int)');
let LEDDefinition_getOnColorBlue = ffi('int LEDDefinition_getOnColorBlue(int)');
let getTicks = ffi('int getTicks(void)');
let printColor = ffi('void printColor(char *)');
let NeoPixel_show = ffi('void NeoPixel_show(void)');
let NeoPixel_clear = ffi('void NeoPixel_clear(void)');
let NeoPixel_set = ffi('void NeoPixel_set(int,int,int,int)');
let LEDStateEngine_getMinTickTime = ffi('double LEDStateEngine_getMinTickTime(void)');
let LEDStateEngine_getMaxTickTime = ffi('double LEDStateEngine_getMaxTickTime(void)');
let LEDStateEngine_getBrightness = ffi('int LEDStateEngine_getBrightness(void)');
let LEDStateEngine_setBrightness = ffi('void LEDStateEngine_setBrightness(int)');
let NeoPixel_getBrightness = ffi('int NeoPixel_getBrightness(void)');
let NeoPixel_setBrightness = ffi('void NeoPixel_setBrightness(int)');
let LEDScheduler_init = ffi('void LEDScheduler_init(int, int, int)');
let LEDScheduler_run = ffi('void LEDScheduler_run(void)');
let LEDScheduler_pause = ffi('void LEDScheduler_pause(void)');
let LEDScheduler_addItem = ffi('void LEDScheduler_addItem(int, int, int, char *, char *)');
let LEDScheduler_setWatch = ffi('void LEDScheduler_setWatch(int, int, int)');

View File

@ -1,6 +1,12 @@
[
{"name": "off", "red": 0, "green": 0, "blue": 0},
{"name": "candle", "red": 30, "green": 15, "blue": 4},
{"name": "25w", "red": 35, "green": 15, "blue": 6},
{"name": "40w", "red": 50, "green": 25, "blue": 10},
{"name": "60w", "red": 70, "green": 45, "blue": 20},
{"name": "75w", "red": 80, "green": 70, "blue": 30},
{"name": "100w", "red": 100, "green": 85, "blue": 50},
{"name": "neon", "red": 100, "green": 95, "blue": 70},
{"name": "red", "red": 50, "green": 5, "blue": 5},
{"name": "green", "red": 5, "green": 50, "blue": 5},
{"name": "blue", "red": 5, "green": 5, "blue": 80},

26
fs/demoschedule.cfg Normal file
View File

@ -0,0 +1,26 @@
{
"comment": "7-LED development board",
"lampmapping": [
"lamp-1", "lamp-2", "lamp-3", "lamp-4", "lamp-5", "lamp-6", "lamp-7"
],
"mrclock": "N-RE",
"clock_starttime": {"h": 2, "m": 0},
"clock_speed": 1,
"schedule": [
{"time": {"h": 2, "m": 30}, "lamp": "lamp-1", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 31}, "lamp": "lamp-2", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 32}, "lamp": "lamp-3", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 45}, "lamp": "lamp-3", "color": "off", "mode": "OnOff"},
{"time": {"h": 2, "m": 46}, "lamp": "lamp-2", "color": "off", "mode": "OnOff"},
{"time": {"h": 2, "m": 47}, "lamp": "lamp-1", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 2}, "lamp": "lamp-1", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 4}, "lamp": "lamp-2", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 4}, "lamp": "lamp-3", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 18}, "lamp": "lamp-3", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 28}, "lamp": "lamp-1", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 29}, "lamp": "lamp-4", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 30}, "lamp": "lamp-5", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 31}, "lamp": "lamp-6", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 32}, "lamp": "lamp-7", "color": "60w", "mode": "OnOff"}
]
}

View File

@ -61,6 +61,8 @@ let configNumLeds = Cfg.get('led.count');
let colorFile = Cfg.get('led.colorFile');
let animationFile = Cfg.get('led.animationFile');
let lampsFile = Cfg.get('led.lampsFile');
let scheduleFile = Cfg.get('led.scheduleFile');
let dynamicMode = Cfg.get('led.mode');
let useDefaults = Cfg.get('led.useDefaults');
let tickDuration = Cfg.get('led.tickDuration');
let numberOfLeds = configNumLeds, numberOfLedDefs = 0; // from config files, count led definition entries
@ -155,6 +157,41 @@ function loadAnimDefs() {
json = null;
}
function loadScheduleDefs() {
// Load Animation Definitions
let json = File.read(scheduleFile);
let scheduleDef = [];
print('scheduleFile =', json);
if (json === '') {
print('ERROR: Schedule definition file does not exist!');
} else {
scheduleDef = JSON.parse(json);
}
LEDScheduler_init(scheduleDef.schedule.length, numberOfLeds, pin);
LEDScheduler_setWatch(2, 0, 1); // time 02:00, clock speed = 1 real time second per model minute
let ledMapping = scheduleDef.lampmapping;
let mapLed = function (name) {
for (let i=0; i<ledMapping.length; ++i) {
if (ledMapping[i] === name) return i;
}
print("**** ERROR in schedule definition file: no lamp mapping found for lamp", name);
return 0;
};
for (i=0; i<scheduleDef.schedule.length; ++i) {
print('- addScheduleItem', scheduleDef.schedule[i].time.h, ":", scheduleDef.schedule[i].time.m, "/",
scheduleDef.schedule[i].lamp, "=", mapLed(scheduleDef.schedule[i].lamp), "/",
scheduleDef.schedule[i].mode, "/",
scheduleDef.schedule[i].color);
LEDScheduler_addItem(scheduleDef.schedule[i].time.h, scheduleDef.schedule[i].time.m,
mapLed(scheduleDef.schedule[i].lamp),
scheduleDef.schedule[i].mode,
scheduleDef.schedule[i].color);
}
json = null;
}
function initialize() {
print('***** Start initialization', getInfo());
let i;
@ -165,19 +202,11 @@ function initialize() {
startLEDStateEngine();
} else {
loadColorDefs();
if (dynamicMode === "animation") {
loadLedDefs();
loadAnimDefs();
// Initialize LED State Engine
LEDStateEngine_init(pin, numberOfLeds);
}
allLedOff();
print('***** End of initialization', getInfo());
print('_______________________________________________________');
print('LedPin:', pin);
print('NumLEDs:', numberOfLeds);
print('Ticks:', getTicks());
print('Tick duration:', tickDuration, 'ms');
print('Brightness:', LEDStateEngine_getBrightness(), '%');
print('LED', 'Color', 'R', 'G', 'B', 'Tick', 'Level', 'Room', 'Id');
print('---', '-----', '---', '---', '-----', '---', '-----', '----', '--');
for (i=0; i<numberOfLeds; ++i) {
@ -191,6 +220,21 @@ function initialize() {
LEDDefinition_getRoom(i),
LEDDefinition_getId(i));
}
} else if (dynamicMode === "schedule") {
loadScheduleDefs();
} else {
print("**** ERROR: Unknown dynamic mode", dynamicMode);
return;
}
}
allLedOff();
print('***** End of initialization', getInfo());
print('_______________________________________________________');
print('LedPin:', pin);
print('NumLEDs:', numberOfLeds);
print('Ticks:', getTicks());
print('Tick duration:', tickDuration, 'ms');
print('Brightness:', NeoPixel_getBrightness(), '%');
}
Timer.set(300, false, function() {
@ -200,8 +244,14 @@ Timer.set(300, false, function() {
allLedOff();
print('***** LED test pattern', getInfo());
showLedTestPattern();
if (dynamicMode === "animation") {
print('***** Start LED state engine and LED-update timer', getInfo());
LEDStateEngine_start();
} else if (dynamicMode === "schedule") {
print('***** Start schedule based animation', getInfo());
NeoPixel_clear();
LEDScheduler_run();
}
Timer.set(30000, true, function() {
print("Timer: minTickTime =", LEDStateEngine_getMinTickTime(),
"ms, maxTickTime =", LEDStateEngine_getMaxTickTime(), "ms");
@ -215,7 +265,7 @@ RPC.addHandler('led.setBrightness', function(args) {
// print(args);
if (args !== undefined && args.level !== undefined) {
if (args.level > 0 && args.level <= 100) {
LEDStateEngine_setBrightness(args.level);
NeoPixel_setBrightness(args.level);
return { result: 'ok' };
} else {
return { error: 'Brightness level must be in the range 1..100' };
@ -226,7 +276,7 @@ RPC.addHandler('led.setBrightness', function(args) {
}, "{level: %d}");
print(' led.getBrightness');
RPC.addHandler('led.getBrightness', function(args) {
let brightness = LEDStateEngine_getBrightness();
let brightness = NeoPixel_getBrightness();
return { result: 'ok', brightness: brightness };
}, null);
print(' led.pause');

30
fs/schedule.cfg Normal file
View File

@ -0,0 +1,30 @@
{
"comment": "Company office building with facility manager appartement",
"lampmapping": [
"eg-1.1", "eg-1.2", "eg-3.1", "eg-4", "eg-5", "eg-8", "eg-7, "eg-6", "eg-3.2", "eg-2.2", "eg-2.1",
"og-1.1", "og-1.2", "og-3.1", "og-4", "og-5", "og-8", "og-7, "og-6", "og-3.2", "og-2.2", "og-2.1",
"dg-1", "dg-2", "dg-3", "dg-4", "dg-5", "dg-6", "dg-7", "dg-8"
],
"mrclock": "N-RE",
"clock_starttime": {"h": 2, "m": 0},
"clock_speed": 1,
"schedule": [
{"time": {"h": 2, "m": 30}, "lamp": "dg-5", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 31}, "lamp": "dg-8", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 32}, "lamp": "dg-7", "color": "25w", "mode": "OnOff"},
{"time": {"h": 2, "m": 45}, "lamp": "dg-7", "color": "off", "mode": "OnOff"},
{"time": {"h": 2, "m": 46}, "lamp": "dg-8", "color": "off", "mode": "OnOff"},
{"time": {"h": 2, "m": 47}, "lamp": "dg-5", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 2}, "lamp": "dg-5", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 4}, "lamp": "dg-8", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 4}, "lamp": "dg-7", "color": "25w", "mode": "OnOff"},
{"time": {"h": 4, "m": 18}, "lamp": "dg-7", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 28}, "lamp": "dg-5", "color": "off", "mode": "OnOff"},
{"time": {"h": 4, "m": 29}, "lamp": "dg-3", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 30}, "lamp": "og-3.2", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 30}, "lamp": "eg-3.2", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 30}, "lamp": "eg-4", "color": "60w", "mode": "OnOff"},
{"time": {"h": 4, "m": 45}, "lamp": "eg-3.1", "color": "40w", "mode": "OnOff"},
{"time": {"h": 4, "m": 47}, "lamp": "eg-1.1", "color": "60w", "mode": "OnOff"}
]
}

View File

@ -39,6 +39,8 @@ config_schema:
- ["led.colorFile", "s", "colors.cfg", {title: "File name containing color definitions"}]
- ["led.lampsFile", "s", "lamps.cfg", {title: "File name containing lamp definitions"}]
- ["led.animationFile", "s", "animations.cfg", {title: "File name containing animation definitions"}]
- ["led.mode", "s", "schedule", {title: "Mode = animation (use lampsFile + animationFile) | schedule (use scheduleFile)"}]
- ["led.scheduleFile", "s", "demoschedule.cfg", {title: "File name containing schedule definitions"}]
build_vars:
MGOS_ENABLE_ONEWIRE: 1
cflags: []

View File

@ -9,7 +9,6 @@
static LEDStateEngine theLEDStateEngine;
static int ticks = 0;
static int brightness = 50;
int getTicks(void) { return ticks; }
@ -27,7 +26,6 @@ void LEDStateEngine_setNumberOfLeds(int numberOfLeds) {
void LEDStateEngine_init(int ledPin, int numberOfLeds) {
theLEDStateEngine.comment = "";
theLEDStateEngine.pin = ledPin;
brightness = mgos_sys_config_get_led_brightness();
LEDStateEngine_setNumberOfLeds(numberOfLeds);
for (int i=0; i<numberOfLeds; ++i) {
LEDState_init(i, LEDDefinition_get(i));
@ -61,24 +59,11 @@ void LEDStateEngine_tick() {
double LEDStateEngine_getMinTickTime() { return 1000 * minTickTime; }
double LEDStateEngine_getMaxTickTime() { return 1000 * maxTickTime; }
static uint8_t adjustBrightness(uint8_t value) {
return (value * brightness)/100;
}
int LEDStateEngine_getBrightness() { return brightness; }
void LEDStateEngine_setBrightness(int newBrightness) {
if (newBrightness > 0 && newBrightness <= 100) {
brightness = newBrightness;
} else {
LOG(LL_ERROR, ("invalid brightness value %d", newBrightness));
}
}
static void updateLedDisplay(LEDState *state) {
NeoPixel_set(state->index,
adjustBrightness(state->currentColor.red),
adjustBrightness(state->currentColor.green),
adjustBrightness(state->currentColor.blue));
state->currentColor.red,
state->currentColor.green,
state->currentColor.blue);
}
void LEDState_tick(int ledNum) {

View File

@ -3,12 +3,28 @@
#include "mgos_bitbang.h"
#include "mgos_gpio.h"
#include "mgos_system.h"
#include "mgos_sys_config.h"
#include "NeoPixel.h"
static int NeoPixel_pin = 0;
static int NeoPixel_numPixels = 0;
static uint8_t *NeoPixel_leds = NULL; // pointer to allocated memory
static enum NeoPixel_ColorOrder NeoPixel_colorOrder = NeoPixel_colorOrder_RGB;
static enum NeoPixel_ColorOrder NeoPixel_colorOrder = NeoPixel_colorOrder_GRB;
static int brightness = 50;
static uint8_t adjustBrightness(uint8_t value) {
return (value * brightness)/100;
}
int NeoPixel_getBrightness() { return brightness; }
void NeoPixel_setBrightness(int newBrightness) {
if (newBrightness > 0 && newBrightness <= 100) {
brightness = newBrightness;
} else {
LOG(LL_ERROR, ("invalid brightness value %d", newBrightness));
}
}
// ## **`NeoPixel_clear()`**
// Clear in-memory values of the pixels.
@ -44,7 +60,8 @@ void NeoPixel_create(uint8_t pin, uint8_t numPixels, enum NeoPixel_ColorOrder or
mgos_gpio_set_mode(pin, MGOS_GPIO_MODE_OUTPUT);
// GPIO.write(pin, 0); // Keep in reset.
mgos_gpio_write(pin, false);
LOG(LL_INFO, ("pin=%d, numPixels=%d, colorOrder=%d", pin, numPixels, order));
brightness = mgos_sys_config_get_led_brightness();
LOG(LL_INFO, ("pin=%d, numPixels=%d, colorOrder=%d, brightness=%d", pin, numPixels, order, brightness));
NeoPixel_clear();
}
@ -78,9 +95,9 @@ void NeoPixel_set(int pixel, int red, int green, int blue) {
} else if (NeoPixel_colorOrder == NeoPixel_colorOrder_BGR) {
v0 = blue; v1 = green; v2 = red;
} else return;
NeoPixel_leds[3*pixel] = v0;
NeoPixel_leds[3*pixel+1] = v1;
NeoPixel_leds[3*pixel+2] = v2;
NeoPixel_leds[3*pixel] = adjustBrightness(v0);
NeoPixel_leds[3*pixel+1] = adjustBrightness(v1);
NeoPixel_leds[3*pixel+2] = adjustBrightness(v2);
}
// ## **`NeoPixel_show()`**

View File

@ -11,5 +11,7 @@ extern void NeoPixel_release();
extern void NeoPixel_create(uint8_t pin, uint8_t numPixels, enum NeoPixel_ColorOrder order);
extern void NeoPixel_set(int pixel, int red, int green, int blue);
extern void NeoPixel_show();
extern int NeoPixel_getBrightness();
extern void NeoPixel_setBrightness(int newBrightness);
#endif

105
src/Scheduler.c Normal file
View File

@ -0,0 +1,105 @@
#include "Scheduler.h"
#include "LEDDefinition.h"
#include "NeoPixel.h"
#include "mgos_timers.h"
enum ScheduleItemMode {
ScheduleItemMode_OnOff,
ScheduleItemMode_NeonOn,
ScheduleItemMode_tv_bw,
ScheduleItemMode_tv_color
};
static enum ScheduleItemMode modeFromName(char *name) {
if (strcmp(name, "OnOff") == 0) return ScheduleItemMode_OnOff;
if (strcmp(name, "NeonOn") == 0) return ScheduleItemMode_NeonOn;
if (strcmp(name, "TV_BW") == 0) return ScheduleItemMode_tv_bw;
if (strcmp(name, "TV_Color") == 0) return ScheduleItemMode_tv_color;
LOG(LL_ERROR, ("Invalid mode name: %s", name));
return ScheduleItemMode_OnOff;
}
typedef struct Schedule_t {
uint8_t h;
uint8_t m;
uint8_t led;
enum ScheduleItemMode mode;
LEDColor *color;
} Schedule;
static Schedule *schedule = NULL;
static int numScheduleItems = 0;
static int loadedScheduleItems = 0;
static int numberOfLeds = 0;
static int currentHour = 0;
static int currentMinute = 0;
static int clockSpeed = 1; // # seconds for every real time minute
static bool clockRunning = false;
static mgos_timer_id clockTimer = 0;
static void executeScheduleItem(Schedule *s) {
LOG(LL_INFO, ("%d:%d LED #%d / mode=%d / color=%s", s->h, s->m, s->led, s->mode, s->color->name));
if (s->mode == ScheduleItemMode_OnOff) {
NeoPixel_set(s->led, s->color->red, s->color->green, s->color->blue);
}
}
static void LEDScheduler_timerTick() {
bool ledsNeedUpdate = false;
if (clockRunning) {
currentMinute++;
if (currentMinute >= 60) {
currentMinute = 0;
currentHour++;
if (currentHour >= 24) {
currentHour = 0;
}
}
}
for (int i=0; i<loadedScheduleItems; ++i) {
if (schedule[i].h == currentHour && schedule[i].m == currentMinute) {
// ACTION!
executeScheduleItem(&schedule[i]);
ledsNeedUpdate = true;
}
}
if (ledsNeedUpdate) {
NeoPixel_show();
}
}
void LEDScheduler_init(int setNumScheduleItems, int setNumberOfLeds, int ledPin) {
if (schedule != NULL) free(schedule);
numScheduleItems = setNumScheduleItems;
loadedScheduleItems = 0;
numberOfLeds = setNumberOfLeds;
schedule = malloc(numScheduleItems * sizeof(Schedule));
NeoPixel_release();
NeoPixel_create(ledPin, numberOfLeds, NeoPixel_colorOrder_GRB);
clockRunning = false;
clockTimer = mgos_set_timer(1000, true, LEDScheduler_timerTick, NULL);
}
void LEDScheduler_addItem(int h, int m, int led, char *mode, char *colorName) {
if (loadedScheduleItems >= numScheduleItems) {
LOG(LL_ERROR, ("Cannot load schedule item as storage is already full, %d:%d LED #%d", h, m, led));
return;
}
schedule[loadedScheduleItems].h = h;
schedule[loadedScheduleItems].m = m;
schedule[loadedScheduleItems].led = led;
schedule[loadedScheduleItems].mode = modeFromName(mode);
schedule[loadedScheduleItems].color = LEDColor_get(colorName);;
schedule[loadedScheduleItems].h = h;
++loadedScheduleItems;
}
void LEDScheduler_run() { clockRunning = true; }
void LEDScheduler_pause() { clockRunning = false; }
void LEDScheduler_setWatch(int h, int m, int speed) {
currentHour = h;
currentMinute = m;
clockSpeed = speed;
LOG(LL_INFO, ("set time %d:%d and speed to %d real-time seconds for a model minute", h, m, speed));
}

5
src/Scheduler.h Normal file
View File

@ -0,0 +1,5 @@
extern void LEDScheduler_init(int, int, int);
extern void LEDScheduler_run();
extern void LEDScheduler_pause();
extern void LEDScheduler_addItem(int h, int m, int led, char *mode, char *colorName);
extern void LEDScheduler_setWatch(int h, int m, int speed);

View File

@ -52,6 +52,7 @@ static void stateEngineTickTimer() {
}
}
static void delayed_boot() {
LOG(LL_INFO, ("*** Start timer for LED state engine ticks"));
mgos_set_timer(mgos_sys_config_get_led_tickDuration(), true, stateEngineTickTimer, NULL);