Skip to content

Commit

Permalink
Initial lighting effects support. (#24)
Browse files Browse the repository at this point in the history
* Initial light effects support added.
* New commands:
  - CONF:OUTPUTx:EFFect
  - CONF:OUTPUTx:EFFect?
  • Loading branch information
tjko authored Jan 4, 2025
1 parent e2dab59 commit 52ba9d1
Show file tree
Hide file tree
Showing 11 changed files with 735 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 35 additions & 0 deletions commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
25 changes: 20 additions & 5 deletions src/brickpico.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
}
}
}
}
}

Expand Down
15 changes: 14 additions & 1 deletion src/brickpico.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "lwip/ip_addr.h"
#endif

#include "effects.h"
#include "ringbuffer.h"

#ifndef BRICKPICO_MODEL
Expand Down Expand Up @@ -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];
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
49 changes: 48 additions & 1 deletion src/command.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* command.c
Copyright (C) 2023 Timo Kokkonen <tjko@iki.fi>
Copyright (C) 2023-2024 Timo Kokkonen <tjko@iki.fi>
SPDX-License-Identifier: GPL-3.0-or-later
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 },
Expand Down
41 changes: 39 additions & 2 deletions src/config.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* config.c
Copyright (C) 2023 Timo Kokkonen <tjko@iki.fi>
Copyright (C) 2023-2025 Timo Kokkonen <tjko@iki.fi>
SPDX-License-Identifier: GPL-3.0-or-later
Expand Down Expand Up @@ -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)
{
Expand All @@ -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++) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
}

Expand Down Expand Up @@ -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.");
}
Expand Down
Loading

0 comments on commit 52ba9d1

Please sign in to comment.