libwebsockets/lib/drivers/led
Andy Green 7b82498d33 esp32: update against Dec 21 idf
Freertos in idf has moved around a bit.
2021-12-23 06:20:27 +00:00
..
led-gpio.c esp32: update against Dec 21 idf 2021-12-23 06:20:27 +00:00
led-seq.c esp32-wrover-kit 2020-06-30 19:35:41 +01:00
private-lib-drivers-led.h esp32-wrover-kit 2020-06-30 19:35:41 +01:00
README.md esp32-wrover-kit 2020-06-30 19:35:41 +01:00

lws_led gpio and pwm class drivers

Lws provides an abstract led controller class that can bind an array of LEDs to gpio and pwm controllers, and automatically handled pwm sequencers.

Lumience intensity is corrected for IEC curves to match perceptual intensity, and the correction can be overridden per led for curve adaptation matching.

Intensity is normalized to a 16-bit scale, when controlled by a GPIO b15 is significant and the rest ignored. When controlled by PWM, as many bits from b15 down are significant as the PWM arrangements can represent.

The PWM sequencers use arbitrary function generation callbacks on a normalized 16-bit phase space, they can choose how much to interpolate and how much to put in a table, a 64-sample, 16-bit sine function is provided along with 16-bit linear sawtooth.

Changing the sequencer is subject to a third transition function sequencer, this can for example mix the transition linearly over, eg, 500ms so the leds look very smooth.

Defining an led controller

An array of inidividual LED information is provided first, and referenced by the LED controller definintion. Leds are named so code does not introduce dependencies on specific implementations.

static const lws_led_gpio_map_t lgm[] = {
	{
		.name			= "alert",
		.gpio			= GPIO_NUM_25,
		.pwm_ops		= &pwm_ops,
		.active_level		= 1,
	},
};

static const lws_led_gpio_controller_t lgc = {
	.led_ops			= lws_led_gpio_ops,
	.gpio_ops			= &lws_gpio_plat,
	.led_map			= &lgm[0],
	.count_leds			= LWS_ARRAY_SIZE(lgm)
};

	struct lws_led_state *lls;

	lls = lgc.led_ops.create(&lgc.led_ops);
	if (!lls) {
		lwsl_err("%s: could not create led\n", __func__);
		goto spin;
	}

For GPIO control, the active level of the GPIO to light the LED may be set.

Each LED may bind to a pwm controller, in which case setting the intensity programs the pwm controller corresponding to the GPIO.

Setting the intensity directly

	lgc.led_ops.intensity(&lgc.led_ops, "alert", 0);

Defining Sequencer

Some common sequencers are provided out of the box, you can also define your own arbitrary ones.

The main point is sequencers have a function that returns an intensity for each of 65536 phase steps in its cycle. For example, this is the linear function that is included

lws_led_intensity_t
lws_led_func_linear(lws_led_seq_phase_t n)
{
	return (lws_led_intensity_t)n;
}

It simply returns an intensity between 0 - 65535 matching the phase angle of 0 - 65535 that it was given, so it's a sawtooth ramp.

An interpolated sine function is also provided that returns an intensity between 0 - 65535 reflecting one cycle of sine wave for the phase angle of 0 - 65535.

These functions are packaged into sequencer structures like this

const lws_led_sequence_def_t lws_pwmseq_sine_endless_fast = {
	.func			= lws_led_func_sine,
	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
	.ledphase_total		= LWS_SEQ_LEDPHASE_TOTAL_ENDLESS,
	.ms			= 750
};

This "endless" sequencer cycles through the sine function at 750ms per cycle. Non-endless sequencers have a specific start and end in the phase space, eg

const lws_led_sequence_def_t lws_pwmseq_sine_up = {
	.func			= lws_led_func_sine,
	.ledphase_offset	= 0, /* already at 0 amp at 0 phase */
	.ledphase_total		= LWS_LED_FUNC_PHASE / 2, /* 180 degree ./^ */
	.ms			= 300
};

... this one traverses 180 degrees of the sine wave starting from 0 and ending at full intensity, over 300ms.

A commonly-used, provided one is like this, as used in the next section

const lws_led_sequence_def_t lws_pwmseq_linear_wipe = {
	.func			= lws_led_func_linear,
	.ledphase_offset	= 0,
	.ledphase_total		= LWS_LED_FUNC_PHASE - 1,
	.ms			= 300
};

Setting the intensity using sequencer transitions

The main api for high level sequenced control is

int
lws_led_transition(struct lws_led_state *lcs, const char *name,
		   const lws_led_sequence_def_t *next,
		   const lws_led_sequence_def_t *trans);

This fades from the current sequence to a new sequence, using trans sequencer intensity as the mix factor. trans is typically lws_pwmseq_linear_wipe, fading between the current and new linearly over 300ms. At the end of the trans sequence, the new sequence simply replaces the current one and the transition is completed.

Sequencers use a single 30Hz OS timer while any sequence is active.

exported sequencer symbol description
lws_pwmseq_sine_endless_slow continuous 100% sine, 1.5s cycle
lws_pwmseq_sine_endless_fast continuous 100% sine, 0.75s cycle
lws_pwmseq_linear_wipe single 0 - 100% ramp over 0.3s
lws_pwmseq_sine_up single 0 - 100% using sine curve over 0.3s
lws_pwmseq_sine_down single 100% - 0 using sine curve over 0.3s
lws_pwmseq_static_on 100% static
lws_pwmseq_static_half 50% static
lws_pwmseq_static_off 0% static