diff --git a/CMakeLists.txt b/CMakeLists.txt index 52298fc..bc5b6f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,10 @@ add_executable(brickpico src/tls.c src/pwm.c src/temp.c + src/effects.c + src/effects_fade.c + src/effects_blink.c + src/effects_pulse.c src/util.c src/util_rp2040.c src/log.c diff --git a/commands.md b/commands.md index 002068f..19f9242 100644 --- a/commands.md +++ b/commands.md @@ -16,6 +16,8 @@ BrickPico supports following commands: * [CONFigure:SAVe](#configuresave) * [CONFigure:OUTPUTx:NAME](#configureoutputxname) * [CONFigure:OUTPUTx:NAME?](#configureoutputxname-1) +* [CONFigure:OUTPUTx:EFFect](#configureoutputxeffect) +* [CONFigure:OUTPUTx:EFFect?](#configureoutputxeffect-1) * [CONFigure:OUTPUTx:MINpwm](#configureoutputxminpwm) * [CONFigure:OUTPUTx:MINpwm?](#configureoutputxminpwm-1) * [CONFigure:OUTPUTx:MAXpwm](#configureoutputxmaxpwm) @@ -276,6 +278,39 @@ CONF:OUTPUT1:NAME? Front Lights ``` +#### CONFigure:OUTPUTx:EFFect +Configure active effect for an ouput channgel. + +Effect|Description|Arguments|Argument Descriptions|Default|Notes +------|-----------|---------|---------------------|-------|------- +none|No effect||| +fade|Fade in/out when channgel is toggled on/off.|fade_in_time,fade_out_time|Fade-in and fade-out times in seconds.|fade,1.0,1.0| +blink|Blink output at specified rate.|on_time,off_time|Light on and off times in seconds.|blink,0.5,1.5| +pulse|Pulse output|fade_in_time,on_time,fade_out_time,off_time|Define duration of each 4 sections of a pulse "cycle" inseconds.|fade,2.0,0.5,2.0,0.5| + +For example (configur blinking using defaults): +``` +CONF:OUTPUT1:EFF blink +``` + +For example (configure blinking with custom parameters): +``` +CONF:OUTPUT1:EFF blink,1.0,2.5 +``` + + +#### CONFigure:OUTPUTx:EFFect? +Display currently active effect for an output. + +Format: effect,arg_1,arg_2,...arg_n + + +For example: +``` +CONF:OUTPUT1:EFF? +blink,0.500000,1.500000 +``` + #### CONFigure:OUTPUTx:MINpwm Set absolute minimum PWM duty cycle (%) for given output port. This can be used to make sure that output never sees a lower diff --git a/src/brickpico.c b/src/brickpico.c index 15e3971..0060ddd 100644 --- a/src/brickpico.c +++ b/src/brickpico.c @@ -248,17 +248,19 @@ void core1_main() struct brickpico_config *config = &core1_config; struct brickpico_state *state = &core1_state; struct brickpico_state prev_state; - absolute_time_t t_now, t_last, t_config, t_state, t_tick; + absolute_time_t t_now, t_last, t_config, t_state, t_tick, t_effect; int64_t max_delta = 0; int64_t delta; + uint8_t pwm[OUTPUT_MAX_COUNT]; log_msg(LOG_INFO, "core1: started..."); + memset(pwm, 0, sizeof(pwm)); /* Allow core0 to pause this core... */ multicore_lockout_victim_init(); clear_state(&prev_state); - t_last = t_config = t_state = t_tick = get_absolute_time(); + t_last = t_effect = t_config = t_state = t_tick = get_absolute_time(); while (1) { t_now = get_absolute_time(); @@ -296,19 +298,32 @@ void core1_main() if (prev_state.pwm[i] != state->pwm[i]) { log_msg(LOG_INFO, "output%d: PWM change '%u' -> '%u'", i + 1, prev_state.pwm[i], state->pwm[i]); - if (state->pwr[i]) - set_pwm_duty_cycle(i, state->pwm[i]); } if (prev_state.pwr[i] != state->pwr[i]) { log_msg(LOG_INFO, "output%d: state change %u -> %u", i + 1, prev_state.pwr[i], state->pwr[i]); - set_pwm_duty_cycle(i, (state->pwr[i] ? state->pwm[i] : 0)); } } } else { log_msg(LOG_INFO, "failed to get state_mutex"); } } + + if (time_passed(&t_effect, 100)) { + uint8_t new; + uint64_t t = to_us_since_boot(get_absolute_time()); + + for(int i = 0; i < OUTPUT_COUNT; i++) { + new = light_effect(config->outputs[i].effect, + config->outputs[i].effect_ctx, + t, state->pwm[i],state->pwr[i]); + + if (new != pwm[i]) { + set_pwm_duty_cycle(i, new); + pwm[i] = new; + } + } + } } } diff --git a/src/brickpico.h b/src/brickpico.h index a6f63df..193927d 100644 --- a/src/brickpico.h +++ b/src/brickpico.h @@ -31,6 +31,7 @@ #include "lwip/ip_addr.h" #endif +#include "effects.h" #include "ringbuffer.h" #ifndef BRICKPICO_MODEL @@ -69,6 +70,7 @@ enum timer_action_types { ACTION_ON = 1, ACTION_OFF = 2, }; +#define TIMER_ACTION_ENUM_MAX 2 struct timer_event { char name[MAX_EVENT_NAME_LEN]; @@ -82,12 +84,16 @@ struct timer_event { struct pwm_output { char name[MAX_NAME_LEN]; - /* output PWM signal settings */ + /* Output PWM signal settings */ uint8_t min_pwm; uint8_t max_pwm; uint8_t default_pwm; /* 0..100 (PWM duty cycle) */ uint8_t default_state; /* 0 = off, 1 = on */ uint8_t type; /* 0 = Dimmer, 1 = Toggle (on/off) */ + + /* Light effect settings */ + enum light_effect_types effect; + void *effect_ctx; }; struct brickpico_config { @@ -207,6 +213,13 @@ void oled_clear_display(); void oled_display_status(const struct brickpico_state *state, const struct brickpico_config *conf); void oled_display_message(int rows, const char **text_lines); +/* effects.c */ +int str2effect(const char *s); +const char* effect2str(enum light_effect_types effect); +void* effect_parse_args(enum light_effect_types effect, const char *args); +char* effect_print_args(enum light_effect_types effect, void *ctx); +uint8_t light_effect(enum light_effect_types effect, void *ctx, uint64_t t, uint8_t pwm, uint8_t pwr); + /* flash.h */ void lfs_setup(bool multicore); int flash_format(bool multicore); diff --git a/src/command.c b/src/command.c index f747c85..d81c293 100644 --- a/src/command.c +++ b/src/command.c @@ -1,5 +1,5 @@ /* command.c - Copyright (C) 2023 Timo Kokkonen + Copyright (C) 2023-2024 Timo Kokkonen SPDX-License-Identifier: GPL-3.0-or-later @@ -678,6 +678,52 @@ int cmd_out_read(const char *cmd, const char *args, int query, char *prev_cmd) return 1; } +int cmd_out_effect(const char *cmd, const char *args, int query, char *prev_cmd) +{ + int out; + int ret = 0; + char *tok, *saveptr, *param; + struct pwm_output *o; + enum light_effect_types new_effect; + void *new_ctx; + + + out = atoi(&prev_cmd[6]) - 1; + if (out < 0 || out >= OUTPUT_COUNT) + return 1; + + o = &conf->outputs[out]; + if (query) { + printf("%s", effect2str(o->effect)); + tok = effect_print_args(o->effect, o->effect_ctx); + if (tok) { + printf(",%s\n", tok); + free(tok); + } else { + printf(",\n"); + } + } else { + if (!(param = strdup(args))) + return 2; + if ((tok = strtok_r(param, ",", &saveptr)) != NULL) { + new_effect = str2effect(tok); + tok = strtok_r(NULL, "\n", &saveptr); + new_ctx = effect_parse_args(new_effect, tok ? tok : ""); + if (new_effect == EFFECT_NONE || new_ctx != NULL) { + o->effect = new_effect; + if (o->effect_ctx) + free(o->effect_ctx); + o->effect_ctx = new_ctx; + } else { + ret = 1; + } + } + free(param); + } + + return ret; +} + int cmd_write_state(const char *cmd, const char *args, int query, char *prev_cmd) { int out, val; @@ -1591,6 +1637,7 @@ const struct cmd_t defaults_c_commands[] = { }; const struct cmd_t output_c_commands[] = { + { "EFFect", 3, NULL, cmd_out_effect }, { "MAXpwm", 3, NULL, cmd_out_max_pwm }, { "MINpwm", 3, NULL, cmd_out_min_pwm }, { "NAME", 4, NULL, cmd_out_name }, diff --git a/src/config.c b/src/config.c index 5ac4a87..6eb93c7 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* config.c - Copyright (C) 2023 Timo Kokkonen + Copyright (C) 2023-2025 Timo Kokkonen SPDX-License-Identifier: GPL-3.0-or-later @@ -38,6 +38,37 @@ auto_init_mutex(config_mutex_inst); mutex_t *config_mutex = &config_mutex_inst; +void json2effect(cJSON *item, enum light_effect_types *effect, void **effect_ctx) +{ + cJSON *args; + const char *a = ""; + + *effect = str2effect(cJSON_GetStringValue(cJSON_GetObjectItem(item, "name"))); + if ((args = cJSON_GetObjectItem(item, "args"))) + a = cJSON_GetStringValue(args); + *effect_ctx = effect_parse_args(*effect, a); + if (!*effect_ctx) + *effect = EFFECT_NONE; +} + + +cJSON* effect2json(enum light_effect_types effect, void *effect_ctx) +{ + cJSON *o; + char *s; + + if ((o = cJSON_CreateObject()) == NULL) + return NULL; + + cJSON_AddItemToObject(o, "name", cJSON_CreateString(effect2str(effect))); + s = effect_print_args(effect, effect_ctx); + cJSON_AddItemToObject(o, "args", cJSON_CreateString(s ? s : "")); + if (s) + free(s); + + return o; +} + void clear_config(struct brickpico_config *cfg) { @@ -55,6 +86,8 @@ void clear_config(struct brickpico_config *cfg) o->default_pwm = 100; o->default_state = 0; o->type = 0; + o->effect = EFFECT_NONE; + o->effect_ctx = NULL; } for (i = 0; i < MAX_EVENT_COUNT; i++) { @@ -272,6 +305,7 @@ cJSON *config_to_json(const struct brickpico_config *cfg) cJSON_AddItemToObject(o, "default_pwm", cJSON_CreateNumber(f->default_pwm)); cJSON_AddItemToObject(o, "default_state", cJSON_CreateNumber(f->default_state)); cJSON_AddItemToObject(o, "type", cJSON_CreateNumber(f->type)); + cJSON_AddItemToObject(o, "effect", effect2json(f->effect, f->effect_ctx)); cJSON_AddItemToArray(outputs, o); } cJSON_AddItemToObject(config, "outputs", outputs); @@ -527,6 +561,9 @@ int json_to_config(cJSON *config, struct brickpico_config *cfg) if ((ref = cJSON_GetObjectItem(item, "type"))) { f->type = cJSON_GetNumberValue(ref); } + if ((ref = cJSON_GetObjectItem(item, "effect"))) { + json2effect(ref, &f->effect, &f->effect_ctx); + } } } @@ -654,7 +691,7 @@ void delete_config() { int res; - res = flash_delete_file("fanpico.cfg"); + res = flash_delete_file("brickpico.cfg"); if (res) { log_msg(LOG_ERR, "Failed to delete configuration."); } diff --git a/src/effects.c b/src/effects.c new file mode 100644 index 0000000..1667951 --- /dev/null +++ b/src/effects.c @@ -0,0 +1,122 @@ +/* effects.c + Copyright (C) 2025 Timo Kokkonen + + SPDX-License-Identifier: GPL-3.0-or-later + + This file is part of BrickPico. + + BrickPico is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BrickPico is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BrickPico. If not, see . +*/ + +#include +#include +#include +#include "pico/stdlib.h" + +#include "brickpico.h" + + +/* effects_fade.c */ +void* effect_fade_parse_args(const char *args); +char* effect_fade_print_args(void *ctx); +uint8_t effect_fade(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr); + +/* effects_blink.c */ +void* effect_blink_parse_args(const char *args); +char* effect_blink_print_args(void *ctx); +uint8_t effect_blink(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr); + +/* effects_pulse.c */ +void* effect_pulse_parse_args(const char *args); +char* effect_pulse_print_args(void *ctx); +uint8_t effect_pulse(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr); + + +static const effect_entry_t effects[] = { + { "none", NULL, NULL, NULL }, /* EFFECT_NONE */ + { "fade", effect_fade_parse_args, effect_fade_print_args, effect_fade }, /* EFFECT_FADE */ + { "blink", effect_blink_parse_args, effect_blink_print_args, effect_blink }, /* EFFECT_BLINK */ + { "pulse", effect_pulse_parse_args, effect_pulse_print_args, effect_pulse }, /* EFFECT_PULSE */ + { NULL, NULL, NULL, NULL } +}; + + + +int str2effect(const char *s) +{ + int ret = EFFECT_NONE; + + for(int i = 0; effects[i].name; i++) { + if (!strncasecmp(s, effects[i].name, strlen(effects[i].name) + 1)) { + ret = i; + break; + } + } + + return ret; +} + + +const char* effect2str(enum light_effect_types effect) +{ + if (effect <= EFFECT_ENUM_MAX) { + return effects[effect].name; + } + + return "none"; +} + + +void* effect_parse_args(enum light_effect_types effect, const char *args) +{ + void *ret = NULL; + + if (effect <= EFFECT_ENUM_MAX) { + if (effects[effect].parse_args_func) + ret = effects[effect].parse_args_func(args); + } + + return ret; +} + + +char* effect_print_args(enum light_effect_types effect, void *ctx) +{ + char *ret = NULL; + + if (effect <= EFFECT_ENUM_MAX && ctx) { + if (effects[effect].print_args_func) + ret = effects[effect].print_args_func(ctx); + } + + return ret; +} + + +inline uint8_t light_effect(enum light_effect_types effect, void *ctx, uint64_t t, uint8_t pwm, uint8_t pwr) +{ + uint8_t ret = 0; + + if (effect <= EFFECT_ENUM_MAX) { + if (effects[effect].effect_func) + ret = effects[effect].effect_func(ctx, t, pwm, pwr); + else + ret = (pwr ? pwm : 0); + } + + return ret; +} + + +/* eof :-) */ diff --git a/src/effects.h b/src/effects.h new file mode 100644 index 0000000..6a56585 --- /dev/null +++ b/src/effects.h @@ -0,0 +1,49 @@ +/* effects.h + Copyright (C) 2025 Timo Kokkonen + + SPDX-License-Identifier: GPL-3.0-or-later + + This file is part of BrickPico. + + BrickPico is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BrickPico is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BrickPico. If not, see . +*/ + +#ifndef BRICKPICO_EFFECTS_H +#define BRICKPICO_EFFECTS_H 1 + +enum light_effect_types { + EFFECT_NONE = 0, /* No effects */ + EFFECT_FADE = 1, /* Fade in/out at defined rates */ + EFFECT_BLINK = 2, /* Blink at defined rate */ + EFFECT_PULSE = 3, /* Pulse at defined rate */ +}; +#define EFFECT_ENUM_MAX 3 + + +typedef void* (effect_parse_args_func_t)(const char *args); +typedef char* (effect_print_args_func_t)(void *ctx); +typedef uint8_t (effect_func_t)(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr); + +typedef struct effect_entry { + const char* name; + effect_parse_args_func_t *parse_args_func; + effect_print_args_func_t *print_args_func; + effect_func_t *effect_func; +} effect_entry_t; + + + + + +#endif /* BRICKPICO_EFFECTS_H */ diff --git a/src/effects_blink.c b/src/effects_blink.c new file mode 100644 index 0000000..b3fc67b --- /dev/null +++ b/src/effects_blink.c @@ -0,0 +1,140 @@ +/* effects_blink.c + Copyright (C) 2025 Timo Kokkonen + + SPDX-License-Identifier: GPL-3.0-or-later + + This file is part of BrickPico. + + BrickPico is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BrickPico is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BrickPico. If not, see . +*/ + +#include +#include +#include +#include "pico/stdlib.h" + +#include "brickpico.h" +#include "effects.h" + + +typedef struct blink_context { + float on_time; + float off_time; + int64_t on_l; + int64_t off_l; + uint8_t last_state; + uint8_t mode; + uint64_t start_t; +} blink_context_t; + + +void* effect_blink_parse_args(const char *args) +{ + blink_context_t *c; + char *tok, *saveptr, s[64]; + float arg; + + if (!args) + return NULL; + if (!(c = calloc(1, sizeof(blink_context_t)))) + return NULL; + + /* Defaults (seconds) */ + c->on_time = 0.5; + c->off_time = 1.5; + + strncopy(s, args, sizeof(s)); + + /* Parse parameters */ + if ((tok = strtok_r(s, ",", &saveptr))) { + if (str_to_float(tok, &arg)) { + if (arg >= 0.0) + c->on_time = arg; + if ((tok = strtok_r(NULL, ",", &saveptr))) { + if (str_to_float(tok, &arg)) { + if (arg >= 0.0) + c->off_time = arg; + } + } + } + } + + c->on_l = c->on_time * 1000000; + c->off_l = c->off_time * 1000000; + c->last_state = 0; + c->mode = 0; + c->start_t = 0; + + return c; +} + +char* effect_blink_print_args(void *ctx) +{ + blink_context_t *c = (blink_context_t*)ctx; + char buf[64]; + + snprintf(buf, sizeof(buf), "%f,%f", c->on_time, c->off_time); + + return strdup(buf); +} + +uint8_t effect_blink(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr) +{ + blink_context_t *c = (blink_context_t*)ctx; + int64_t t_d; + uint8_t ret = 0; + + if (c->last_state != pwr) { + c->start_t = t_now; + if (pwr) { + c->mode = 1; + ret = pwm; + } else { + c->mode = 0; + ret = 0; + } + } + else { + ret = 0; + if (pwr) { + t_d = t_now - c->start_t; + + if (c->mode == 1) { + if (t_d < c->on_l) { + ret = pwm; + } else { + c->start_t = t_now; + c->mode = 0; + ret = 0; + } + } + else { + if (t_d < c->off_l) { + ret = 0; + } else { + c->start_t = t_now; + c->mode = 1; + ret = pwm; + } + } + } + } + + c->last_state = pwr; + + return ret; +} + + +/* eof :-) */ diff --git a/src/effects_fade.c b/src/effects_fade.c new file mode 100644 index 0000000..7eec3a4 --- /dev/null +++ b/src/effects_fade.c @@ -0,0 +1,142 @@ +/* effects_fade.c + Copyright (C) 2025 Timo Kokkonen + + SPDX-License-Identifier: GPL-3.0-or-later + + This file is part of BrickPico. + + BrickPico is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BrickPico is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BrickPico. If not, see . +*/ + +#include +#include +#include +#include "pico/stdlib.h" + +#include "brickpico.h" +#include "effects.h" + + +typedef struct fade_context { + float fade_in; + float fade_out; + int64_t in_l; + int64_t out_l; + uint8_t last_state; + uint8_t mode; + uint64_t start_t; +} fade_context_t; + + +void* effect_fade_parse_args(const char *args) +{ + fade_context_t *c; + char *tok, *saveptr, s[64]; + float arg; + + if (!args) + return NULL; + if (!(c = calloc(1, sizeof(fade_context_t)))) + return NULL; + + /* Defaults (seconds) */ + c->fade_in = 1.0; + c->fade_out = 1.0; + + strncopy(s, args, sizeof(s)); + + /* Parse parameters */ + if ((tok = strtok_r(s, ",", &saveptr))) { + if (str_to_float(tok, &arg)) { + if (arg >= 0.0) + c->fade_in = arg; + if ((tok = strtok_r(NULL, ",", &saveptr))) { + if (str_to_float(tok, &arg)) { + if (arg >= 0.0) + c->fade_out = arg; + } + } + } + } + + c->in_l = c->fade_in * 1000000; + c->out_l = c->fade_out * 1000000; + c->last_state = 0; + c->mode = 0; + c->start_t = 0; + + return c; +} + +char* effect_fade_print_args(void *ctx) +{ + fade_context_t *c = (fade_context_t*)ctx; + char buf[64]; + + snprintf(buf, sizeof(buf), "%f,%f", c->fade_in, c->fade_out); + + return strdup(buf); +} + +uint8_t effect_fade(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr) +{ + fade_context_t *c = (fade_context_t*)ctx; + int64_t t_d; + uint8_t ret = 0; + + if (c->last_state != pwr) { + /* Start fade in/out sequence... */ + c->start_t = t_now; + if (pwr) { + c->mode = 1; + ret = 0; + } else { + c->mode = 3; + ret = pwm; + } + } + else { + t_d = t_now - c->start_t; + + if (c->mode == 1) { /* Fade in... */ + if (t_d < c->in_l) { + ret = pwm * t_d / c->in_l; + } else { + c->mode = 2; + ret = pwm; + } + } + else if (c->mode == 2) { /* On state after fade in... */ + ret = pwm; + } + else if (c->mode == 3) { /* Fade out... */ + if (t_d < c->out_l) { + ret = pwm - (pwm * t_d / c->out_l); + } else { + c->mode = 4; + ret = 0; + } + } + else if (c->mode == 4) { /* Off state after fade out... */ + ret = 0; + } + } + + c->last_state = pwr; + + return ret; +} + + +/* eof :-) */ diff --git a/src/effects_pulse.c b/src/effects_pulse.c new file mode 100644 index 0000000..fc48011 --- /dev/null +++ b/src/effects_pulse.c @@ -0,0 +1,122 @@ +/* effects_pulse.c + Copyright (C) 2025 Timo Kokkonen + + SPDX-License-Identifier: GPL-3.0-or-later + + This file is part of BrickPico. + + BrickPico is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + BrickPico is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with BrickPico. If not, see . +*/ + +#include +#include +#include +#include "pico/stdlib.h" + +#include "brickpico.h" +#include "effects.h" + + +#define ARG_COUNT 4 + +typedef struct pulse_context { + float args[ARG_COUNT]; + int64_t end[ARG_COUNT]; +} pulse_context_t; + + +void* effect_pulse_parse_args(const char *args) +{ + pulse_context_t *c; + char *tok, *saveptr, *s; + + if (!args) + return NULL; + if (!(c = calloc(1, sizeof(pulse_context_t)))) + return NULL; + + + /* Defaults (in seconds) */ + c->args[0] = 2.0; /* Fade In */ + c->args[1] = 0.5; /* ON Time */ + c->args[2] = 2.0; /* Fade Out */ + c->args[3] = 0.5; /* OFF Time */ + + + /* Parse arguments */ + if ((s = strdup(args))) { + for(int i = 0; i < ARG_COUNT; i ++) { + float arg; + + if (!(tok = strtok_r((i == 0 ? s : NULL), ",", &saveptr))) + break; + if (str_to_float(tok, &arg)) { + if (arg >= 0.0) { + c->args[i] = arg; + } + } + } + free(s); + } + + for(int i = 0; i < ARG_COUNT; i++) { + c->end[i] = c->args[i] * 1000000; + if (i > 0) + c->end[i] += c->end[i - 1]; + printf("pulse_arg[%d]: %f, %lld\n", i, c->args[i], c->end[i]); + } + + return c; +} + + +char* effect_pulse_print_args(void *ctx) +{ + pulse_context_t *c = (pulse_context_t*)ctx; + char buf[128]; + + snprintf(buf, sizeof(buf), "%f,%f,%f,%f", c->args[0], c->args[1], c->args[2], c->args[3]); + + return strdup(buf); +} + + +uint8_t effect_pulse(void *ctx, uint64_t t_now, uint8_t pwm, uint8_t pwr) +{ + pulse_context_t *c = (pulse_context_t*)ctx; + int64_t t; + uint8_t ret = 0; + + if (pwr) { + t = t_now % c->end[3]; + + if (t < c->end[0]) { /* Fade In */ + ret = pwm * t / c->end[0]; + } + else if (t < c->end[1]) { /* ON */ + ret = pwm; + } + else if (t < c->end[2]) { /* Fade Out */ + ret = pwm - (pwm * (t - c->end[1]) / (c->end[2] - c->end[1])); + } + else { /* OFF */ + ret = 0; + } + } + + return ret; +} + + +/* eof :-) */