mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-09 13:07:03 +03:00
merge changes from led_visu to v4.3
This commit is contained in:
11
components/led_strip/CMakeLists.txt
Normal file
11
components/led_strip/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
idf_component_register(SRC_DIRS .
|
||||||
|
INCLUDE_DIRS .
|
||||||
|
REQUIRES platform_config tools esp_common
|
||||||
|
PRIV_REQUIRES services freertos driver
|
||||||
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(led_strip.c
|
||||||
|
PROPERTIES COMPILE_FLAGS
|
||||||
|
-Wno-format-overflow
|
||||||
|
)
|
||||||
202
components/led_strip/LICENSE
Normal file
202
components/led_strip/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
407
components/led_strip/led_strip.c
Normal file
407
components/led_strip/led_strip.c
Normal file
@@ -0,0 +1,407 @@
|
|||||||
|
/* ----------------------------------------------------------------------------
|
||||||
|
File: led_strip.c
|
||||||
|
Author(s): Lucas Bruder <LBruder@me.com>
|
||||||
|
Date Created: 11/23/2016
|
||||||
|
Last modified: 11/26/2016
|
||||||
|
|
||||||
|
Updated: C. Rohs - The update thread now
|
||||||
|
only runs when signalled. The double buffer code was modified to copy on show
|
||||||
|
instead of the ping pong buffer that destroyed the buffers contents.
|
||||||
|
|
||||||
|
The current code is not thread safe, but is more performant, and the thread
|
||||||
|
safety does not matter the was it is currently used.
|
||||||
|
|
||||||
|
Description: LED Library for driving various led strips on ESP32.
|
||||||
|
|
||||||
|
This library uses double buffering to display the LEDs.
|
||||||
|
------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define LED_STRIP_TASK_SIZE (1024)
|
||||||
|
#define LED_STRIP_TASK_PRIORITY (configMAX_PRIORITIES - 1)
|
||||||
|
|
||||||
|
#define LED_STRIP_REFRESH_PERIOD_MS (30U) // TODO: add as parameter to led_strip_init
|
||||||
|
|
||||||
|
#define LED_STRIP_NUM_RMT_ITEMS_PER_LED (24U) // Assumes 24 bit color for each led
|
||||||
|
|
||||||
|
// RMT Clock source is @ 80 MHz. Dividing it by 8 gives us 10 MHz frequency, or 100ns period.
|
||||||
|
#define LED_STRIP_RMT_CLK_DIV (8)
|
||||||
|
|
||||||
|
/****************************
|
||||||
|
WS2812 Timing
|
||||||
|
****************************/
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812 9 // 900ns (900ns +/- 150ns per datasheet)
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812 3 // 300ns (350ns +/- 150ns per datasheet)
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812 3 // 300ns (350ns +/- 150ns per datasheet)
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812 9 // 900ns (900ns +/- 150ns per datasheet)
|
||||||
|
|
||||||
|
/****************************
|
||||||
|
SK6812 Timing
|
||||||
|
****************************/
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812 6
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812 6
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812 3
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812 9
|
||||||
|
|
||||||
|
/****************************
|
||||||
|
APA106 Timing
|
||||||
|
****************************/
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106 14 // 1.36us +/- 150ns per datasheet
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106 3 // 350ns +/- 150ns per datasheet
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106 3 // 350ns +/- 150ns per datasheet
|
||||||
|
#define LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106 14 // 1.36us +/- 150ns per datasheet
|
||||||
|
|
||||||
|
// Function pointer for generating waveforms based on different LED drivers
|
||||||
|
typedef void (*led_fill_rmt_items_fn)(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length);
|
||||||
|
|
||||||
|
static inline void led_strip_fill_item_level(rmt_item32_t* item, int high_ticks, int low_ticks)
|
||||||
|
{
|
||||||
|
item->level0 = 1;
|
||||||
|
item->duration0 = high_ticks;
|
||||||
|
item->level1 = 0;
|
||||||
|
item->duration1 = low_ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_1_sk6812(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_1_LOW_SK6812);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_0_sk6812(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_SK6812, LED_STRIP_RMT_TICKS_BIT_0_LOW_SK6812);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_strip_fill_rmt_items_sk6812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
|
||||||
|
{
|
||||||
|
uint32_t rmt_items_index = 0;
|
||||||
|
for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
|
||||||
|
struct led_color_t led_color = led_strip_buf[led_index];
|
||||||
|
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_sk6812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_1_ws2812(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_1_LOW_WS2812);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_0_ws2812(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_WS2812, LED_STRIP_RMT_TICKS_BIT_0_LOW_WS2812);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_strip_fill_rmt_items_ws2812(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
|
||||||
|
{
|
||||||
|
uint32_t rmt_items_index = 0;
|
||||||
|
for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
|
||||||
|
struct led_color_t led_color = led_strip_buf[led_index];
|
||||||
|
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_ws2812(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_1_apa106(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_1_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_1_LOW_APA106);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void led_strip_rmt_bit_0_apa106(rmt_item32_t* item)
|
||||||
|
{
|
||||||
|
led_strip_fill_item_level(item, LED_STRIP_RMT_TICKS_BIT_0_HIGH_APA106, LED_STRIP_RMT_TICKS_BIT_0_LOW_APA106);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_strip_fill_rmt_items_apa106(struct led_color_t *led_strip_buf, rmt_item32_t *rmt_items, uint32_t led_strip_length)
|
||||||
|
{
|
||||||
|
uint32_t rmt_items_index = 0;
|
||||||
|
for (uint32_t led_index = 0; led_index < led_strip_length; led_index++) {
|
||||||
|
struct led_color_t led_color = led_strip_buf[led_index];
|
||||||
|
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.red >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.green >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
for (uint8_t bit = 8; bit != 0; bit--) {
|
||||||
|
uint8_t bit_set = (led_color.blue >> (bit - 1)) & 1;
|
||||||
|
if(bit_set) {
|
||||||
|
led_strip_rmt_bit_1_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
} else {
|
||||||
|
led_strip_rmt_bit_0_apa106(&(rmt_items[rmt_items_index]));
|
||||||
|
}
|
||||||
|
rmt_items_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void led_strip_task(void *arg)
|
||||||
|
{
|
||||||
|
struct led_strip_t *led_strip = (struct led_strip_t *)arg;
|
||||||
|
led_fill_rmt_items_fn led_make_waveform = NULL;
|
||||||
|
|
||||||
|
size_t num_items_malloc = (LED_STRIP_NUM_RMT_ITEMS_PER_LED * led_strip->led_strip_length);
|
||||||
|
rmt_item32_t *rmt_items = (rmt_item32_t*) malloc(sizeof(rmt_item32_t) * num_items_malloc);
|
||||||
|
if (!rmt_items) {
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (led_strip->rgb_led_type) {
|
||||||
|
case RGB_LED_TYPE_WS2812:
|
||||||
|
led_make_waveform = led_strip_fill_rmt_items_ws2812;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RGB_LED_TYPE_SK6812:
|
||||||
|
led_make_waveform = led_strip_fill_rmt_items_sk6812;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RGB_LED_TYPE_APA106:
|
||||||
|
led_make_waveform = led_strip_fill_rmt_items_apa106;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Will avoid keeping it point to NULL
|
||||||
|
led_make_waveform = led_strip_fill_rmt_items_ws2812;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
for(;;) {
|
||||||
|
rmt_wait_tx_done(led_strip->rmt_channel, portMAX_DELAY);
|
||||||
|
vTaskDelay(LED_STRIP_REFRESH_PERIOD_MS / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
xSemaphoreTake(led_strip->access_semaphore, portMAX_DELAY);
|
||||||
|
|
||||||
|
led_make_waveform(led_strip->led_strip_working,
|
||||||
|
rmt_items,
|
||||||
|
led_strip->led_strip_length);
|
||||||
|
rmt_write_items(led_strip->rmt_channel,
|
||||||
|
rmt_items,
|
||||||
|
num_items_malloc,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rmt_items) {
|
||||||
|
free(rmt_items);
|
||||||
|
}
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool led_strip_init_rmt(struct led_strip_t *led_strip)
|
||||||
|
{
|
||||||
|
rmt_config_t rmt_cfg = {
|
||||||
|
.rmt_mode = RMT_MODE_TX,
|
||||||
|
.channel = led_strip->rmt_channel,
|
||||||
|
.clk_div = LED_STRIP_RMT_CLK_DIV,
|
||||||
|
.gpio_num = led_strip->gpio,
|
||||||
|
.mem_block_num = 1,
|
||||||
|
.tx_config = {
|
||||||
|
.loop_en = false,
|
||||||
|
.carrier_freq_hz = 100, // Not used, but has to be set to avoid divide by 0 err
|
||||||
|
.carrier_duty_percent = 50,
|
||||||
|
.carrier_level = RMT_CARRIER_LEVEL_LOW,
|
||||||
|
.carrier_en = false,
|
||||||
|
.idle_level = RMT_IDLE_LEVEL_LOW,
|
||||||
|
.idle_output_en = true,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t cfg_ok = rmt_config(&rmt_cfg);
|
||||||
|
if (cfg_ok != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
esp_err_t install_ok = rmt_driver_install(rmt_cfg.channel, 0, 0);
|
||||||
|
if (install_ok != ESP_OK) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool led_strip_init(struct led_strip_t *led_strip)
|
||||||
|
{
|
||||||
|
TaskHandle_t led_strip_task_handle;
|
||||||
|
|
||||||
|
if ((led_strip == NULL) ||
|
||||||
|
(led_strip->rmt_channel >= RMT_CHANNEL_MAX) ||
|
||||||
|
(led_strip->gpio > GPIO_NUM_33) ||
|
||||||
|
(led_strip->led_strip_working == NULL) ||
|
||||||
|
(led_strip->led_strip_showing == NULL) ||
|
||||||
|
(led_strip->led_strip_length == 0) ||
|
||||||
|
(led_strip->access_semaphore == NULL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(led_strip->led_strip_working == led_strip->led_strip_showing) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(led_strip->led_strip_working, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
|
||||||
|
memset(led_strip->led_strip_showing, 0, sizeof(struct led_color_t) * led_strip->led_strip_length);
|
||||||
|
|
||||||
|
bool init_rmt = led_strip_init_rmt(led_strip);
|
||||||
|
if (!init_rmt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
xSemaphoreGive(led_strip->access_semaphore);
|
||||||
|
BaseType_t task_created = xTaskCreate(led_strip_task,
|
||||||
|
"led_strip_task",
|
||||||
|
LED_STRIP_TASK_SIZE,
|
||||||
|
led_strip,
|
||||||
|
LED_STRIP_TASK_PRIORITY,
|
||||||
|
&led_strip_task_handle);
|
||||||
|
|
||||||
|
if (!task_created) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
|
||||||
|
{
|
||||||
|
bool set_led_success = true;
|
||||||
|
|
||||||
|
if ((!led_strip) || (!color) || (pixel_num > led_strip->led_strip_length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip->led_strip_working[pixel_num] = *color;
|
||||||
|
|
||||||
|
return set_led_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool led_strip_set_pixel_rgb(struct led_strip_t *led_strip, uint32_t pixel_num, uint8_t red, uint8_t green, uint8_t blue)
|
||||||
|
{
|
||||||
|
bool set_led_success = true;
|
||||||
|
|
||||||
|
if ((!led_strip) || (pixel_num > led_strip->led_strip_length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip->led_strip_working[pixel_num].red = red;
|
||||||
|
led_strip->led_strip_working[pixel_num].green = green;
|
||||||
|
led_strip->led_strip_working[pixel_num].blue = blue;
|
||||||
|
|
||||||
|
return set_led_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color)
|
||||||
|
{
|
||||||
|
bool get_success = true;
|
||||||
|
|
||||||
|
if ((!led_strip) ||
|
||||||
|
(pixel_num > led_strip->led_strip_length) ||
|
||||||
|
(!color)) {
|
||||||
|
color = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*color = led_strip->led_strip_working[pixel_num];
|
||||||
|
|
||||||
|
return get_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the led buffer to be shown
|
||||||
|
*/
|
||||||
|
bool led_strip_show(struct led_strip_t *led_strip)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
|
if (!led_strip) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* copy the current buffer for display */
|
||||||
|
memcpy(led_strip->led_strip_showing,led_strip->led_strip_working, sizeof(struct led_color_t) * led_strip->led_strip_length);
|
||||||
|
|
||||||
|
xSemaphoreGive(led_strip->access_semaphore);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the LED strip
|
||||||
|
*/
|
||||||
|
bool led_strip_clear(struct led_strip_t *led_strip)
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
if (!led_strip) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(led_strip->led_strip_working,
|
||||||
|
0,
|
||||||
|
sizeof(struct led_color_t) * led_strip->led_strip_length);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
96
components/led_strip/led_strip.h
Normal file
96
components/led_strip/led_strip.h
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/* ---------------------------------------------------------------------------
|
||||||
|
File: led_strip.h
|
||||||
|
Author(s): Lucas Bruder <LBruder@me.com>
|
||||||
|
Date Created: 11/23/2016
|
||||||
|
Last modified: 11/26/2016
|
||||||
|
|
||||||
|
Description:
|
||||||
|
This library can drive led strips through the RMT module on the ESP32.
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
#ifndef LED_STRIP_H
|
||||||
|
#define LED_STRIP_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <driver/rmt.h>
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
enum rgb_led_type_t {
|
||||||
|
RGB_LED_TYPE_WS2812 = 0,
|
||||||
|
RGB_LED_TYPE_SK6812 = 1,
|
||||||
|
RGB_LED_TYPE_APA106 = 2,
|
||||||
|
|
||||||
|
RGB_LED_TYPE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RGB LED colors
|
||||||
|
*/
|
||||||
|
struct led_color_t {
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t blue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct led_strip_t {
|
||||||
|
const enum rgb_led_type_t rgb_led_type;
|
||||||
|
uint32_t led_strip_length;
|
||||||
|
|
||||||
|
// RMT peripheral settings
|
||||||
|
rmt_channel_t rmt_channel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt table is located in soc.h
|
||||||
|
* As of 11/27/16, reccomended interrupts are:
|
||||||
|
* 9, 12, 13, 17, 18, 19, 20, 21 or 23
|
||||||
|
* Ensure that the same interrupt number isn't used twice
|
||||||
|
* across all libraries
|
||||||
|
*/
|
||||||
|
int rmt_interrupt_num;
|
||||||
|
|
||||||
|
gpio_num_t gpio; // Must be less than GPIO_NUM_33
|
||||||
|
|
||||||
|
struct led_color_t *led_strip_working;
|
||||||
|
struct led_color_t *led_strip_showing;
|
||||||
|
|
||||||
|
SemaphoreHandle_t access_semaphore;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool led_strip_init(struct led_strip_t *led_strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the pixel at pixel_num to color.
|
||||||
|
*/
|
||||||
|
bool led_strip_set_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color);
|
||||||
|
bool led_strip_set_pixel_rgb(struct led_strip_t *led_strip, uint32_t pixel_num, uint8_t red, uint8_t green, uint8_t blue);
|
||||||
|
/**
|
||||||
|
* Get the pixel color at pixel_num for the led strip that is currently being shown!
|
||||||
|
* NOTE: If you call set_pixel_color then get_pixel_color for the same pixel_num, you will not
|
||||||
|
* get back the same pixel value. This gets you the color of the pixel currently being shown, not the one
|
||||||
|
* being updated
|
||||||
|
*
|
||||||
|
* If there is an invalid argument, color will point to NULL and this function will return false.
|
||||||
|
*/
|
||||||
|
bool led_strip_get_pixel_color(struct led_strip_t *led_strip, uint32_t pixel_num, struct led_color_t *color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the led buffer to be shown using double buffering.
|
||||||
|
*/
|
||||||
|
bool led_strip_show(struct led_strip_t *led_strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the LED strip.
|
||||||
|
*/
|
||||||
|
bool led_strip_clear(struct led_strip_t *led_strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // LED_STRIP_H
|
||||||
363
components/led_strip/led_vu.c
Normal file
363
components/led_strip/led_vu.c
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
/*
|
||||||
|
* Control of LED strip within squeezelite-esp32
|
||||||
|
*
|
||||||
|
* (c) Wizmo 2021
|
||||||
|
*
|
||||||
|
* Loosely based on code by
|
||||||
|
* Chuck Rohs 2020, chuck@zethus.ca
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*
|
||||||
|
* ToDo:
|
||||||
|
* Driver does support other led device. Maybe look at supporting in future.
|
||||||
|
* The VU refresh rate has been decreaced (100->75) to optimize animation of spin dial. Could make
|
||||||
|
* configurable like text scrolling (or use the same value)
|
||||||
|
* Look at reserving a status led within the effects. (may require nvs setting for center or end position)
|
||||||
|
* Artwork function, but not released as very buggy and not really practical
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "platform_config.h"
|
||||||
|
#include "led_vu.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_vu";
|
||||||
|
|
||||||
|
#define LED_VU_STACK_SIZE (3*1024)
|
||||||
|
#define LED_VU_RMT_INTR_NUM 20U
|
||||||
|
|
||||||
|
#define LED_VU_PEAK_HOLD 6U
|
||||||
|
|
||||||
|
#define LED_VU_DEFAULT_GPIO 22
|
||||||
|
#define LED_VU_DEFAULT_LENGTH 19
|
||||||
|
#define LED_VU_MAX_LENGTH 255
|
||||||
|
|
||||||
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
||||||
|
|
||||||
|
struct led_strip_t* led_display = NULL;
|
||||||
|
static struct led_strip_t led_strip_config = {
|
||||||
|
.rgb_led_type = RGB_LED_TYPE_WS2812,
|
||||||
|
.rmt_channel = RMT_CHANNEL_1,
|
||||||
|
.rmt_interrupt_num = LED_VU_RMT_INTR_NUM,
|
||||||
|
.gpio = GPIO_NUM_22,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
int gpio;
|
||||||
|
int length;
|
||||||
|
int vu_length;
|
||||||
|
int vu_start_l;
|
||||||
|
int vu_start_r;
|
||||||
|
int vu_odd;
|
||||||
|
} strip;
|
||||||
|
|
||||||
|
static int led_addr(int pos ) {
|
||||||
|
if (pos < 0) return pos + strip.length;
|
||||||
|
if (pos >= strip.length) return pos - strip.length;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Initialize the led vu strip if configured.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void led_vu_init()
|
||||||
|
{
|
||||||
|
char* p;
|
||||||
|
char* config = config_alloc_get_str("led_vu_config", NULL, "N/A");
|
||||||
|
|
||||||
|
// Initialize led VU strip
|
||||||
|
char* drivername = strcasestr(config, "WS2812");
|
||||||
|
|
||||||
|
if ((p = strcasestr(config, "length")) != NULL) {
|
||||||
|
strip.length = atoi(strchr(p, '=') + 1);
|
||||||
|
} // else 0
|
||||||
|
if ((p = strcasestr(config, "gpio")) != NULL) {
|
||||||
|
strip.gpio = atoi(strchr(p, '=') + 1);
|
||||||
|
} else {
|
||||||
|
strip.gpio = LED_VU_DEFAULT_GPIO;
|
||||||
|
}
|
||||||
|
// check for valid configuration
|
||||||
|
if (!drivername || !strip.gpio) {
|
||||||
|
ESP_LOGI(TAG, "led_vu configuration invalid");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strip.length > LED_VU_MAX_LENGTH) strip.length = LED_VU_MAX_LENGTH;
|
||||||
|
// initialize vu settings
|
||||||
|
//strip.vu_length = (strip.length % 2) ? strip.length / 2 : (strip.length - 1) / 2;
|
||||||
|
strip.vu_length = (strip.length - 1) / 2;
|
||||||
|
strip.vu_start_l = strip.vu_length;
|
||||||
|
strip.vu_start_r = strip.vu_start_l + 1;
|
||||||
|
strip.vu_odd = strip.length - 1;
|
||||||
|
|
||||||
|
// create driver configuration
|
||||||
|
led_strip_config.access_semaphore = xSemaphoreCreateBinary();
|
||||||
|
led_strip_config.led_strip_length = strip.length;
|
||||||
|
led_strip_config.led_strip_working = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT);
|
||||||
|
led_strip_config.led_strip_showing = heap_caps_malloc(strip.length * sizeof(struct led_color_t), MALLOC_CAP_8BIT);
|
||||||
|
led_strip_config.gpio = strip.gpio;
|
||||||
|
|
||||||
|
// initialize driver
|
||||||
|
bool led_init_ok = led_strip_init(&led_strip_config);
|
||||||
|
if (led_init_ok) {
|
||||||
|
led_display = &led_strip_config;
|
||||||
|
ESP_LOGI(TAG, "led_vu using gpio:%d length:%d", strip.gpio, strip.length);
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "led_vu init failed");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserver max memory for remote management systems
|
||||||
|
rmt_set_mem_block_num(RMT_CHANNEL_1, 7);
|
||||||
|
|
||||||
|
led_vu_clear(led_display);
|
||||||
|
|
||||||
|
done:
|
||||||
|
free(config);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool inRange(double x, double y, double z) {
|
||||||
|
return (x > y && x < z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Returns the led strip length
|
||||||
|
*/
|
||||||
|
uint16_t led_vu_string_length() {
|
||||||
|
if (!led_display) return 0;
|
||||||
|
return (uint16_t)strip.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Turns all LEDs off (Black)
|
||||||
|
*/
|
||||||
|
void led_vu_clear() {
|
||||||
|
if (!led_display) return;
|
||||||
|
led_strip_clear(led_display);
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Sets all LEDs to one color
|
||||||
|
* r = red (0-255), g = green (0-255), b - blue (0-255)
|
||||||
|
* note - all colors are adjusted for brightness
|
||||||
|
*/
|
||||||
|
void led_vu_color_all(uint8_t r, uint8_t g, uint8_t b) {
|
||||||
|
if (!led_display) return;
|
||||||
|
|
||||||
|
struct led_color_t color_on = {.red = r, .green = g, .blue = b};
|
||||||
|
|
||||||
|
for (int i = 0 ; i < strip.length ; i ++){
|
||||||
|
led_strip_set_pixel_color(led_display, i, &color_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Sets LEDs based on a data packet consiting of rgb data
|
||||||
|
* offset - starting LED,
|
||||||
|
* length - number of leds (3x rgb bytes)
|
||||||
|
* data - array of rgb values in multiples of 3 bytes
|
||||||
|
*/
|
||||||
|
void led_vu_data(uint8_t* data, uint16_t offset, uint16_t length) {
|
||||||
|
if (!led_display) return;
|
||||||
|
|
||||||
|
uint8_t* p = (uint8_t*) data;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
led_strip_set_pixel_rgb(led_display, i+offset, *p, *(p+1), *(p+2));
|
||||||
|
p+=3;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Progress bar display
|
||||||
|
* data - array of gain values(0-100)
|
||||||
|
* offset - starting position
|
||||||
|
* length - size of array
|
||||||
|
*/
|
||||||
|
void led_vu_spectrum(uint8_t* data, int bright, int length, int style) {
|
||||||
|
if (!led_display) return;
|
||||||
|
uint8_t gain,r,g,b;
|
||||||
|
int width = strip.length / length;
|
||||||
|
int pos = 0;
|
||||||
|
uint8_t* p = (uint8_t*) data;
|
||||||
|
for (int i=0; i<length; i++) {
|
||||||
|
gain = *p;
|
||||||
|
r = gain*gain/bright;
|
||||||
|
if (!style) {
|
||||||
|
g = 0;
|
||||||
|
b = gain;
|
||||||
|
} else {
|
||||||
|
g = r;
|
||||||
|
r = 0;
|
||||||
|
b = gain * (bright-gain)/bright;
|
||||||
|
}
|
||||||
|
for (int j=0; j<width; j++) {
|
||||||
|
led_strip_set_pixel_rgb(led_display, pos, r, g, b);
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Progress bar display
|
||||||
|
* pct - percentage complete (0-100)
|
||||||
|
*/
|
||||||
|
void led_vu_progress_bar(int pct, int bright) {
|
||||||
|
if (!led_display) return;
|
||||||
|
|
||||||
|
// define colors
|
||||||
|
struct led_color_t color_on = {.red = bright, .green = 0, .blue = 0};
|
||||||
|
struct led_color_t color_off = {.red = 0, .green = bright, .blue = 0};
|
||||||
|
|
||||||
|
// calcuate led position
|
||||||
|
int led_lit = strip.length * pct / 100;
|
||||||
|
|
||||||
|
// set colors
|
||||||
|
for (int i = 0; i < strip.length; i++) {
|
||||||
|
led_strip_set_pixel_color(led_display, i, (i < led_lit) ? &color_off : &color_on);
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Spin dial display
|
||||||
|
* gain - brightness (0-100), rate - color change speed (0-100)
|
||||||
|
* comet - alternate display mode
|
||||||
|
*/
|
||||||
|
void led_vu_spin_dial(int gain, int rate, bool comet)
|
||||||
|
{
|
||||||
|
if (!led_display) return;
|
||||||
|
|
||||||
|
static int led_pos = 0;
|
||||||
|
static uint8_t r = 0;
|
||||||
|
static uint8_t g = 0;
|
||||||
|
static uint8_t b = 0;
|
||||||
|
|
||||||
|
// calculate next color
|
||||||
|
uint8_t step = rate / 2; // controls color change speed
|
||||||
|
if (r == 0 && g == 0 && b == 0) {
|
||||||
|
r = LED_VU_MAX; g = step;
|
||||||
|
} else if (b == 0) {
|
||||||
|
g = (g > LED_VU_MAX-step) ? LED_VU_MAX : g + step;
|
||||||
|
r = (r < step) ? 0 : r - step;
|
||||||
|
if (r == 0) b = step;
|
||||||
|
} else if (r == 0) {
|
||||||
|
b = (b > LED_VU_MAX-step) ? LED_VU_MAX : b + step;
|
||||||
|
g = (g < step) ? 0 : g- step;
|
||||||
|
if (g == 0) r = step;
|
||||||
|
} else {
|
||||||
|
r = (r > LED_VU_MAX-step) ? LED_VU_MAX : r + step;
|
||||||
|
b = (b < step) ? 0 : b - step;
|
||||||
|
if (r == 0) b = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t rp = r * gain / LED_VU_MAX;
|
||||||
|
uint8_t gp = g * gain / LED_VU_MAX;
|
||||||
|
uint8_t bp = b * gain / LED_VU_MAX;
|
||||||
|
|
||||||
|
// set led color_
|
||||||
|
led_strip_set_pixel_rgb(led_display, led_pos, rp, gp, bp);
|
||||||
|
if (comet) {
|
||||||
|
led_strip_set_pixel_rgb(led_display, led_addr(led_pos-1), rp/2, gp/2, bp/2);
|
||||||
|
led_strip_set_pixel_rgb(led_display, led_addr(led_pos-2), rp/4, gp/4, bp/4);
|
||||||
|
led_strip_set_pixel_rgb(led_display, led_addr(led_pos-3), rp/8, gp/8, bp/8);
|
||||||
|
led_strip_set_pixel_rgb(led_display, led_addr(led_pos-4), 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// next led
|
||||||
|
led_pos = led_addr(++led_pos);
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* VU meter display
|
||||||
|
* vu_l - left response (0-100), vu_r - right response (0-100)
|
||||||
|
* comet - alternate display mode
|
||||||
|
*/
|
||||||
|
void led_vu_display(int vu_l, int vu_r, int bright, bool comet) {
|
||||||
|
static int peak_l = 0;
|
||||||
|
static int peak_r = 0;
|
||||||
|
static int decay_l = 0;
|
||||||
|
static int decay_r = 0;
|
||||||
|
if (!led_display) return;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// scale vu samples to length
|
||||||
|
vu_l = vu_l * strip.vu_length / bright;
|
||||||
|
vu_r = vu_r * strip.vu_length / bright;
|
||||||
|
|
||||||
|
// calculate hold peaks
|
||||||
|
if (peak_l > vu_l) {
|
||||||
|
if (decay_l-- < 0) {
|
||||||
|
decay_l = LED_VU_PEAK_HOLD;
|
||||||
|
peak_l--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peak_l = vu_l;
|
||||||
|
decay_l = LED_VU_PEAK_HOLD;
|
||||||
|
}
|
||||||
|
if (peak_r > vu_r) {
|
||||||
|
if (decay_r-- < 0) {
|
||||||
|
decay_r = LED_VU_PEAK_HOLD;
|
||||||
|
peak_r--;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
peak_r = vu_r;
|
||||||
|
decay_r = LED_VU_PEAK_HOLD;
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn off all leds
|
||||||
|
led_strip_clear(led_display);
|
||||||
|
|
||||||
|
// set the led bar values
|
||||||
|
uint8_t step = bright / (strip.vu_length-1);
|
||||||
|
if (step < 1) step = 1; // dor low brightness or larger strips
|
||||||
|
uint8_t g = bright * 2 / 3; // more red at top
|
||||||
|
uint8_t r = 0;
|
||||||
|
int shift = 0;
|
||||||
|
for (int i = 0; i < strip.vu_length; i++) {
|
||||||
|
// set left
|
||||||
|
if (i == peak_l) {
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r, g, bright);
|
||||||
|
} else if (i <= vu_l) {
|
||||||
|
shift = vu_l - i;
|
||||||
|
if (comet)
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r>>shift, g>>shift, 0);
|
||||||
|
else
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_l - i, r, g, 0);
|
||||||
|
}
|
||||||
|
// set right
|
||||||
|
if (i == peak_r) {
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r, g, bright);
|
||||||
|
} else if (i <= vu_r) {
|
||||||
|
shift = vu_r - i;
|
||||||
|
if (comet)
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r>>shift, g>>shift, 0);
|
||||||
|
else
|
||||||
|
led_strip_set_pixel_rgb(led_display, strip.vu_start_r + i, r, g, 0);
|
||||||
|
}
|
||||||
|
// adjust colors (with limit checks)
|
||||||
|
r = (r > bright-step) ? bright : r + step;
|
||||||
|
g = (g < step) ? 0 : g - step;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_strip_show(led_display);
|
||||||
|
}
|
||||||
|
|
||||||
31
components/led_strip/led_vu.h
Normal file
31
components/led_strip/led_vu.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Control of LED strip within squeezelite-esp32
|
||||||
|
*
|
||||||
|
* (c) Wizmo 2021
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#define LED_VU_MAX 255U
|
||||||
|
#define LED_VU_BRIGHT 20U
|
||||||
|
|
||||||
|
#define led_vu_color_red(B) led_vu_color_all(B, 0, 0)
|
||||||
|
#define led_vu_color_green(B) led_vu_color_all(0, B, 0)
|
||||||
|
#define led_vu_color_blue(B) led_vu_color_all(0, 0, B)
|
||||||
|
#define led_vu_color_yellow(B) led_vu_color_all(B/2, B/2, 0)
|
||||||
|
|
||||||
|
extern struct led_strip_t* led_display;
|
||||||
|
|
||||||
|
uint16_t led_vu_string_length();
|
||||||
|
void led_vu_progress_bar(int pct, int bright);
|
||||||
|
void led_vu_display(int vu_l, int vu_r, int bright, bool comet);
|
||||||
|
void led_vu_spin_dial(int gain, int rate, bool comet);
|
||||||
|
void led_vu_spectrum(uint8_t* data, int bright, int length, int style);
|
||||||
|
void led_vu_color_all(uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void led_vu_data(uint8_t* data, uint16_t offset, uint16_t length);
|
||||||
|
void led_vu_clear();
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ const char * desc_spdif= "SPDIF Options";
|
|||||||
const char * desc_audio= "General Audio Options";
|
const char * desc_audio= "General Audio Options";
|
||||||
const char * desc_bt_source= "Bluetooth Audio Output Options";
|
const char * desc_bt_source= "Bluetooth Audio Output Options";
|
||||||
const char * desc_rotary= "Rotary Control";
|
const char * desc_rotary= "Rotary Control";
|
||||||
|
const char * desc_ledvu= "Led Strip Options";
|
||||||
|
|
||||||
extern const struct adac_s *dac_set[];
|
extern const struct adac_s *dac_set[];
|
||||||
|
|
||||||
@@ -108,6 +109,15 @@ static struct {
|
|||||||
struct arg_end * end;
|
struct arg_end * end;
|
||||||
} rotary_args;
|
} rotary_args;
|
||||||
//config_rotary_get
|
//config_rotary_get
|
||||||
|
|
||||||
|
static struct {
|
||||||
|
struct arg_str * type;
|
||||||
|
struct arg_int * length;
|
||||||
|
struct arg_int * gpio;
|
||||||
|
struct arg_lit * clear;
|
||||||
|
struct arg_end * end;
|
||||||
|
} ledvu_args;
|
||||||
|
|
||||||
static struct{
|
static struct{
|
||||||
struct arg_str *sink_name;
|
struct arg_str *sink_name;
|
||||||
struct arg_str *pin_code;
|
struct arg_str *pin_code;
|
||||||
@@ -635,6 +645,54 @@ static int do_cspot_config(int argc, char **argv){
|
|||||||
FREE_AND_NULL(buf);
|
FREE_AND_NULL(buf);
|
||||||
return nerrors;
|
return nerrors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int do_ledvu_cmd(int argc, char **argv){
|
||||||
|
ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0};
|
||||||
|
esp_err_t err=ESP_OK;
|
||||||
|
int nerrors = arg_parse(argc, argv,(void **)&ledvu_args);
|
||||||
|
if (ledvu_args.clear->count) {
|
||||||
|
cmd_send_messaging(argv[0],MESSAGING_WARNING,"ledvu config cleared\n");
|
||||||
|
config_set_value(NVS_TYPE_STR, "led_vu_config", "");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *buf = NULL;
|
||||||
|
size_t buf_size = 0;
|
||||||
|
FILE *f = open_memstream(&buf, &buf_size);
|
||||||
|
if (f == NULL) {
|
||||||
|
cmd_send_messaging(argv[0],MESSAGING_ERROR,"Unable to open memory stream.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(nerrors >0){
|
||||||
|
arg_print_errors(f,ledvu_args.end,desc_ledvu);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
nerrors+=is_output_gpio(ledvu_args.gpio, f, &ledvu.gpio, true);
|
||||||
|
|
||||||
|
if(ledvu_args.length->count==0 || ledvu_args.length->ival[0]<1 || ledvu_args.length->ival[0]>255){
|
||||||
|
fprintf(f,"error: strip length must be greater than 0 and no more than 255\n");
|
||||||
|
nerrors++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ledvu.length = ledvu_args.length->count>0?ledvu_args.length->ival[0]:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!nerrors ){
|
||||||
|
fprintf(f,"Storing ledvu parameters.\n");
|
||||||
|
nerrors+=(config_ledvu_set(&ledvu )!=ESP_OK);
|
||||||
|
}
|
||||||
|
if(!nerrors ){
|
||||||
|
fprintf(f,"Done.\n");
|
||||||
|
}
|
||||||
|
fflush (f);
|
||||||
|
cmd_send_messaging(argv[0],nerrors>0?MESSAGING_ERROR:MESSAGING_INFO,"%s", buf);
|
||||||
|
fclose(f);
|
||||||
|
FREE_AND_NULL(buf);
|
||||||
|
return (nerrors==0 && err==ESP_OK)?0:1;
|
||||||
|
}
|
||||||
|
|
||||||
static int do_i2s_cmd(int argc, char **argv)
|
static int do_i2s_cmd(int argc, char **argv)
|
||||||
{
|
{
|
||||||
i2s_platform_config_t i2s_dac_pin = {
|
i2s_platform_config_t i2s_dac_pin = {
|
||||||
@@ -842,6 +900,24 @@ cJSON * rotary_cb(){
|
|||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cJSON * ledvu_cb(){
|
||||||
|
cJSON * values = cJSON_CreateObject();
|
||||||
|
const ledvu_struct_t *ledvu= config_ledvu_get();
|
||||||
|
|
||||||
|
if(GPIO_IS_VALID_GPIO(ledvu->gpio) && ledvu->gpio>=0 && ledvu->length > 0){
|
||||||
|
cJSON_AddNumberToObject(values,"gpio",ledvu->gpio);
|
||||||
|
cJSON_AddNumberToObject(values,"length",ledvu->length);
|
||||||
|
}
|
||||||
|
if(strlen(ledvu->type)>0){
|
||||||
|
cJSON_AddStringToObject(values,"type",ledvu->type);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cJSON_AddStringToObject(values,"type","WS2812");
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
cJSON * audio_cb(){
|
cJSON * audio_cb(){
|
||||||
cJSON * values = cJSON_CreateObject();
|
cJSON * values = cJSON_CreateObject();
|
||||||
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
char * p = config_alloc_get_default(NVS_TYPE_STR, "jack_mutes_amp", "n", 0);
|
||||||
@@ -1252,6 +1328,24 @@ static void register_rotary_config(void){
|
|||||||
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void register_ledvu_config(void){
|
||||||
|
ledvu_args.type = arg_str1(NULL,"type","<none>|WS2812","Led type (supports one rgb strip to display built in effects and allow remote control through 'dmx' messaging)");
|
||||||
|
ledvu_args.length = arg_int1(NULL,"length","<1..255>","Strip length (1-255 supported)");
|
||||||
|
ledvu_args.gpio = arg_int1(NULL,"gpio","gpio","Data pin");
|
||||||
|
ledvu_args.clear = arg_lit0(NULL, "clear", "Clear configuration");
|
||||||
|
ledvu_args.end = arg_end(4);
|
||||||
|
|
||||||
|
const esp_console_cmd_t cmd = {
|
||||||
|
.command = CFG_TYPE_HW("ledvu"),
|
||||||
|
.help = desc_ledvu,
|
||||||
|
.hint = NULL,
|
||||||
|
.func = &do_ledvu_cmd,
|
||||||
|
.argtable = &ledvu_args
|
||||||
|
};
|
||||||
|
cmd_to_json_with_cb(&cmd,&ledvu_cb);
|
||||||
|
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
|
||||||
|
}
|
||||||
|
|
||||||
static void register_audio_config(void){
|
static void register_audio_config(void){
|
||||||
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
audio_args.jack_behavior = arg_str0("j", "jack_behavior","Headphones|Subwoofer","On supported DAC, determines the audio jack behavior. Selecting headphones will cause the external amp to be muted on insert, while selecting Subwoofer will keep the amp active all the time.");
|
||||||
audio_args.end = arg_end(6);
|
audio_args.end = arg_end(6);
|
||||||
@@ -1340,5 +1434,6 @@ void register_config_cmd(void){
|
|||||||
register_spdif_config();
|
register_spdif_config();
|
||||||
}
|
}
|
||||||
register_rotary_config();
|
register_rotary_config();
|
||||||
|
register_ledvu_config();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,30 @@ esp_err_t config_rotary_set(rotary_struct_t * config){
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
esp_err_t config_ledvu_set(ledvu_struct_t * config){
|
||||||
|
int buffer_size=512;
|
||||||
|
esp_err_t err=ESP_OK;
|
||||||
|
char * config_buffer=calloc(buffer_size,1);
|
||||||
|
char * config_buffer2=calloc(buffer_size,1);
|
||||||
|
if(config_buffer && config_buffer2) {
|
||||||
|
snprintf(config_buffer,buffer_size,"%s,length=%i,gpio=%i",config->type, config->length, config->gpio);
|
||||||
|
log_send_messaging(MESSAGING_INFO,"Updating ledvu configuration to %s",config_buffer);
|
||||||
|
err = config_set_value(NVS_TYPE_STR, "led_vu_config", config_buffer);
|
||||||
|
if(err!=ESP_OK){
|
||||||
|
log_send_messaging(MESSAGING_ERROR,"Error: %s",esp_err_to_name(err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
err = ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
FREE_AND_NULL(config_buffer);
|
||||||
|
FREE_AND_NULL(config_buffer2);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -722,6 +746,24 @@ const rotary_struct_t * config_rotary_get() {
|
|||||||
return &rotary;
|
return &rotary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const ledvu_struct_t * config_ledvu_get() {
|
||||||
|
|
||||||
|
static ledvu_struct_t ledvu={ .type = "WS2812", .gpio = -1, .length = 0};
|
||||||
|
char *config = config_alloc_get_default(NVS_TYPE_STR, "led_vu_config", NULL, 0);
|
||||||
|
if (config && *config) {
|
||||||
|
char *p;
|
||||||
|
|
||||||
|
// ToDo: Add code for future support of alternate led types
|
||||||
|
if ((p = strcasestr(config, "gpio")) != NULL) ledvu.gpio = atoi(strchr(p, '=') + 1);
|
||||||
|
if ((p = strcasestr(config, "length")) != NULL) ledvu.length = atoi(strchr(p, '=') + 1);
|
||||||
|
free(config);
|
||||||
|
}
|
||||||
|
return &ledvu;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -925,6 +967,17 @@ cJSON * get_Rotary_GPIO(cJSON * list){
|
|||||||
return llist;
|
return llist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
cJSON * get_ledvu_GPIO(cJSON * list){
|
||||||
|
cJSON * llist = list?list:cJSON_CreateArray();
|
||||||
|
|
||||||
|
const ledvu_struct_t *ledvu= config_ledvu_get();
|
||||||
|
add_gpio_for_value(llist,"gpio",ledvu->gpio, "led_vu", false);
|
||||||
|
return llist;
|
||||||
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -1130,6 +1183,7 @@ cJSON * get_gpio_list(bool refresh) {
|
|||||||
gpio_list=get_SPI_GPIO(gpio_list);
|
gpio_list=get_SPI_GPIO(gpio_list);
|
||||||
gpio_list=get_I2C_GPIO(gpio_list);
|
gpio_list=get_I2C_GPIO(gpio_list);
|
||||||
gpio_list=get_DAC_GPIO(gpio_list);
|
gpio_list=get_DAC_GPIO(gpio_list);
|
||||||
|
gpio_list=get_ledvu_GPIO(gpio_list);
|
||||||
gpio_list=get_psram_gpio_list(gpio_list);
|
gpio_list=get_psram_gpio_list(gpio_list);
|
||||||
gpio_list=get_eth_GPIO(gpio_list);
|
gpio_list=get_eth_GPIO(gpio_list);
|
||||||
return gpio_list;
|
return gpio_list;
|
||||||
|
|||||||
@@ -84,6 +84,12 @@ typedef struct {
|
|||||||
int timer;
|
int timer;
|
||||||
} rotary_struct_t;
|
} rotary_struct_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char type[16];
|
||||||
|
int length;
|
||||||
|
int gpio;
|
||||||
|
} ledvu_struct_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool fixed;
|
bool fixed;
|
||||||
char * name;
|
char * name;
|
||||||
@@ -115,3 +121,5 @@ bool is_dac_config_locked();
|
|||||||
bool are_statistics_enabled();
|
bool are_statistics_enabled();
|
||||||
const rotary_struct_t * config_rotary_get();
|
const rotary_struct_t * config_rotary_get();
|
||||||
esp_err_t config_rotary_set(rotary_struct_t * rotary);
|
esp_err_t config_rotary_set(rotary_struct_t * rotary);
|
||||||
|
const ledvu_struct_t * config_ledvu_get();
|
||||||
|
esp_err_t config_ledvu_set(ledvu_struct_t * rotary);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
idf_component_register(SRC_DIRS .
|
idf_component_register(SRC_DIRS .
|
||||||
INCLUDE_DIRS .
|
INCLUDE_DIRS .
|
||||||
REQUIRES app_update esp_https_ota
|
REQUIRES app_update esp_https_ota
|
||||||
PRIV_REQUIRES console tools display services platform_config spi_flash vfs console freertos platform_console
|
PRIV_REQUIRES console tools display led_strip services platform_config spi_flash vfs console freertos platform_console
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -157,24 +157,26 @@ static progress_t * loc_displayer_get_progress_dft(){
|
|||||||
}
|
}
|
||||||
static void loc_displayer_progressbar(uint8_t pct){
|
static void loc_displayer_progressbar(uint8_t pct){
|
||||||
static progress_t * progress_coordinates;
|
static progress_t * progress_coordinates;
|
||||||
if(!display){
|
if(display) {
|
||||||
return;
|
if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft();
|
||||||
}
|
int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100);
|
||||||
if(!progress_coordinates) progress_coordinates = loc_displayer_get_progress_dft();
|
|
||||||
int filler_x=progress_coordinates->filler.x1+(int)((float)progress_coordinates->filler.width*(float)pct/(float)100);
|
|
||||||
|
|
||||||
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2);
|
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2);
|
||||||
GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false);
|
GDS_DrawBox(display,progress_coordinates->border.x1,progress_coordinates->border.y1,progress_coordinates->border.x2,progress_coordinates->border.y2,GDS_COLOR_WHITE,false);
|
||||||
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2);
|
ESP_LOGD(TAG,"Drawing %d,%d,%d,%d",progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2);
|
||||||
if(filler_x > progress_coordinates->filler.x1){
|
if(filler_x > progress_coordinates->filler.x1){
|
||||||
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true);
|
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,filler_x,progress_coordinates->filler.y2,GDS_COLOR_WHITE,true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Clear the inner box
|
||||||
|
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true);
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG,"Updating Display");
|
||||||
|
GDS_Update(display);
|
||||||
}
|
}
|
||||||
else {
|
if (led_display) {
|
||||||
// Clear the inner box
|
led_vu_progress_bar(pct, LED_VU_BRIGHT);
|
||||||
GDS_DrawBox(display,progress_coordinates->filler.x1,progress_coordinates->filler.y1,progress_coordinates->filler.x2,progress_coordinates->filler.y2,GDS_COLOR_BLACK,true);
|
|
||||||
}
|
}
|
||||||
ESP_LOGD(TAG,"Updating Display");
|
|
||||||
GDS_Update(display);
|
|
||||||
}
|
}
|
||||||
void sendMessaging(messaging_types type,const char * fmt, ...){
|
void sendMessaging(messaging_types type,const char * fmt, ...){
|
||||||
va_list args;
|
va_list args;
|
||||||
@@ -452,6 +454,10 @@ void ota_task_cleanup(const char * message, ...){
|
|||||||
va_start(args, message);
|
va_start(args, message);
|
||||||
sendMessaging(MESSAGING_ERROR,message, args);
|
sendMessaging(MESSAGING_ERROR,message, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
|
if (led_display) led_vu_color_red(LED_VU_BRIGHT);
|
||||||
|
} else {
|
||||||
|
if (led_display) led_vu_color_green(LED_VU_BRIGHT);
|
||||||
}
|
}
|
||||||
FREE_RESET(ota_status->ota_write_data);
|
FREE_RESET(ota_status->ota_write_data);
|
||||||
FREE_RESET(ota_status->bin_data);
|
FREE_RESET(ota_status->bin_data);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ idf_component_register( SRC_DIRS . external ac101 tas57xx wm8978
|
|||||||
display
|
display
|
||||||
tools
|
tools
|
||||||
audio
|
audio
|
||||||
|
led_strip
|
||||||
EMBED_FILES vu_s.data arrow.data
|
EMBED_FILES vu_s.data arrow.data
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "gds_text.h"
|
#include "gds_text.h"
|
||||||
#include "gds_draw.h"
|
#include "gds_draw.h"
|
||||||
#include "gds_image.h"
|
#include "gds_image.h"
|
||||||
|
#include "led_vu.h"
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
@@ -107,13 +108,20 @@ struct visu_packet {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ledv_packet {
|
||||||
|
char opcode[4];
|
||||||
|
u8_t which;
|
||||||
|
u8_t style;
|
||||||
|
u8_t bright;
|
||||||
|
};
|
||||||
|
|
||||||
struct ANIC_header {
|
struct ANIC_header {
|
||||||
char opcode[4];
|
char opcode[4];
|
||||||
u32_t length;
|
u32_t length;
|
||||||
u8_t mode;
|
u8_t mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dmxt_packet {
|
struct ledd_packet {
|
||||||
char opcode[4];
|
char opcode[4];
|
||||||
u16_t x;
|
u16_t x;
|
||||||
u16_t length;
|
u16_t length;
|
||||||
@@ -206,7 +214,7 @@ static EXT_RAM_ATTR struct {
|
|||||||
|
|
||||||
static EXT_RAM_ATTR struct {
|
static EXT_RAM_ATTR struct {
|
||||||
int mode;
|
int mode;
|
||||||
int max;
|
int n, style, max;
|
||||||
u16_t config;
|
u16_t config;
|
||||||
struct bar_s bars[MAX_BARS] ;
|
struct bar_s bars[MAX_BARS] ;
|
||||||
} led_visu;
|
} led_visu;
|
||||||
@@ -247,11 +255,10 @@ static void grfs_handler(u8_t *data, int len);
|
|||||||
static void grfg_handler(u8_t *data, int len);
|
static void grfg_handler(u8_t *data, int len);
|
||||||
static void grfa_handler(u8_t *data, int len);
|
static void grfa_handler(u8_t *data, int len);
|
||||||
static void visu_handler(u8_t *data, int len);
|
static void visu_handler(u8_t *data, int len);
|
||||||
static void dmxt_handler(u8_t *data, int len);
|
static void ledv_handler(u8_t *data, int len);
|
||||||
|
static void ledd_handler(u8_t *data, int len);
|
||||||
static void displayer_task(void* arg);
|
static void displayer_task(void* arg);
|
||||||
|
|
||||||
void *led_display;
|
|
||||||
|
|
||||||
/* scrolling undocumented information
|
/* scrolling undocumented information
|
||||||
grfs
|
grfs
|
||||||
B: screen number
|
B: screen number
|
||||||
@@ -349,8 +356,7 @@ bool sb_displayer_init(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (led_display) {
|
if (led_display) {
|
||||||
// PLACEHOLDER to init config
|
led_visu.config = led_vu_string_length();
|
||||||
led_visu.mode = VISU_VUMETER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inform LMS of our screen/led dimensions
|
// inform LMS of our screen/led dimensions
|
||||||
@@ -428,10 +434,11 @@ static void sendSETD(u16_t width, u16_t height, u16_t led_config) {
|
|||||||
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
|
pkt_header.id = 0xfe; // id 0xfe is width S:P:Squeezebox2
|
||||||
pkt_header.length = htonl(sizeof(pkt_header) + 6 - 8);
|
pkt_header.length = htonl(sizeof(pkt_header) + 6 - 8);
|
||||||
|
|
||||||
LOG_INFO("sending dimension %ux%u", width, height);
|
LOG_INFO("sending dimension display:%ux%u led_config:%u", width, height, led_config);
|
||||||
|
|
||||||
width = htons(width);
|
width = htons(width);
|
||||||
height = htons(height);
|
height = htons(height);
|
||||||
|
led_config = htons(led_config);
|
||||||
|
|
||||||
LOCK_P;
|
LOCK_P;
|
||||||
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
|
send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
|
||||||
@@ -481,8 +488,10 @@ static bool handler(u8_t *data, int len){
|
|||||||
grfa_handler(data, len);
|
grfa_handler(data, len);
|
||||||
} else if (!strncmp((char*) data, "visu", 4)) {
|
} else if (!strncmp((char*) data, "visu", 4)) {
|
||||||
visu_handler(data, len);
|
visu_handler(data, len);
|
||||||
} else if (!strncmp((char*) data, "dmxt", 4)) {
|
} else if (!strncmp((char*) data, "ledv", 4)) {
|
||||||
dmxt_handler(data, len);
|
ledv_handler(data, len);
|
||||||
|
} else if (!strncmp((char*) data, "ledd", 4)) {
|
||||||
|
ledd_handler(data, len);
|
||||||
} else {
|
} else {
|
||||||
res = false;
|
res = false;
|
||||||
}
|
}
|
||||||
@@ -1074,23 +1083,40 @@ static void displayer_update(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// actualize led_vu
|
// actualize led_vu
|
||||||
if (led_visu.mode) {
|
if (led_display && led_visu.mode) {
|
||||||
// PLACEHOLDER to handle led_display. you need potentially scaling of spectrum (X and Y)
|
// scale to correct rgb brightness
|
||||||
// and scaling of levels (Y) and then call the
|
if (led_visu.mode == VISU_VUMETER) vu_scale(led_visu.bars, led_visu.max, meters.levels);
|
||||||
|
else spectrum_scale(led_visu.n, led_visu.bars, led_visu.max, meters.samples);
|
||||||
|
|
||||||
|
// run built in visualizer effects
|
||||||
|
if (led_visu.mode == VISU_VUMETER) {
|
||||||
|
led_vu_display(led_visu.bars[0].current, led_visu.bars[1].current, led_visu.max, led_visu.style);
|
||||||
|
} else if (led_visu.mode == VISU_SPECTRUM) {
|
||||||
|
uint8_t* led_data = malloc(led_visu.n);
|
||||||
|
uint8_t* p = (uint8_t*) led_data;
|
||||||
|
for (int i = 0; i < led_visu.n; i++) {
|
||||||
|
*p = led_visu.bars[i].current;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
led_vu_spectrum(led_data, led_visu.max, led_visu.n, led_visu.style);
|
||||||
|
free(led_data);
|
||||||
|
} else if (led_visu.mode == VISU_WAVEFORM) {
|
||||||
|
led_vu_spin_dial(led_visu.bars[1].current, led_visu.bars[(led_visu.n/2)+1].current * 50 / led_visu.max , led_visu.style);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Calculate spectrum spread
|
* Calculate spectrum spread
|
||||||
*/
|
*/
|
||||||
static void spectrum_limits(int min, int n, int pos) {
|
static void spectrum_limits(struct bar_s *bars, int min, int n, int pos, float spectrum_scale) {
|
||||||
if (n / 2) {
|
if (n / 2) {
|
||||||
int step = ((DISPLAY_BW - min) * visu.spectrum_scale) / (n/2);
|
int step = ((DISPLAY_BW - min) * spectrum_scale) / (n/2);
|
||||||
visu.bars[pos].limit = min + step;
|
bars[pos].limit = min + step;
|
||||||
for (int i = 1; i < n/2; i++) visu.bars[pos+i].limit = visu.bars[pos+i-1].limit + step;
|
for (int i = 1; i < n/2; i++) bars[pos+i].limit = bars[pos+i-1].limit + step;
|
||||||
spectrum_limits(visu.bars[pos + n/2 - 1].limit, n - n/2, pos + n/2);
|
spectrum_limits(bars, bars[pos + n/2 - 1].limit, n - n/2, pos + n/2, spectrum_scale);
|
||||||
} else {
|
} else {
|
||||||
visu.bars[pos].limit = DISPLAY_BW;
|
bars[pos].limit = DISPLAY_BW;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1103,7 +1129,7 @@ static void visu_fit(int bars, int width, int height) {
|
|||||||
visu.n = bars ? bars : MAX_BARS;
|
visu.n = bars ? bars : MAX_BARS;
|
||||||
visu.max = height - 1;
|
visu.max = height - 1;
|
||||||
if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
|
if (visu.spectrum_scale <= 0 || visu.spectrum_scale > 0.5) visu.spectrum_scale = 0.5;
|
||||||
spectrum_limits(0, visu.n, 0);
|
spectrum_limits(visu.bars, 0, visu.n, 0, visu.spectrum_scale);
|
||||||
} else {
|
} else {
|
||||||
visu.n = 2;
|
visu.n = 2;
|
||||||
visu.max = (visu.style ? VU_COUNT : height) - 1;
|
visu.max = (visu.style ? VU_COUNT : height) - 1;
|
||||||
@@ -1236,11 +1262,50 @@ static void visu_handler( u8_t *data, int len) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************************
|
/****************************************************************************************
|
||||||
* Dmx style packet handler
|
* Led_visu packet handler
|
||||||
|
*/
|
||||||
|
static void ledv_handler( u8_t *data, int len) {
|
||||||
|
struct ledv_packet *pkt = (struct ledv_packet*) data;
|
||||||
|
|
||||||
|
LOG_DEBUG("led_visu %u with parameters", pkt->which);
|
||||||
|
|
||||||
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
|
led_visu.mode = pkt->which;
|
||||||
|
led_visu.style = pkt->style;
|
||||||
|
led_visu.max = pkt->bright;
|
||||||
|
|
||||||
|
led_vu_clear();
|
||||||
|
if (led_visu.mode) {
|
||||||
|
if (led_visu.mode == VISU_SPECTRUM) {
|
||||||
|
led_visu.n = (led_visu.config < MAX_BARS) ? led_visu.config : MAX_BARS;
|
||||||
|
spectrum_limits(led_visu.bars, 0, led_visu.n, 0, 0.25);
|
||||||
|
} else if (led_visu.mode == VISU_WAVEFORM) {
|
||||||
|
led_visu.n = 6;
|
||||||
|
spectrum_limits(led_visu.bars, 0, led_visu.n, 0, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
displayer.wake = 1; // wake up
|
||||||
|
|
||||||
|
// reset bars maximum
|
||||||
|
for (int i = led_visu.n; --i >= 0;) led_visu.bars[i].max = 0;
|
||||||
|
|
||||||
|
LOG_INFO("LED Visualizer mode %u with bars:%u max:%u style:%d", led_visu.mode, led_visu.n, led_visu.max, led_visu.style);
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Stopping led visualizer");
|
||||||
|
}
|
||||||
|
|
||||||
|
xSemaphoreGive(displayer.mutex);
|
||||||
|
|
||||||
|
// resume displayer task
|
||||||
|
vTaskResume(displayer.task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************************************************************************************
|
||||||
|
* Led_data dmx style packet handler
|
||||||
* ToDo: make packet match dmx protocol format
|
* ToDo: make packet match dmx protocol format
|
||||||
*/
|
*/
|
||||||
static void dmxt_handler( u8_t *data, int len) {
|
static void ledd_handler( u8_t *data, int len) {
|
||||||
struct dmxt_packet *pkt = (struct dmxt_packet*) data;
|
struct ledd_packet *pkt = (struct ledd_packet*) data;
|
||||||
uint16_t offset = htons(pkt->x);
|
uint16_t offset = htons(pkt->x);
|
||||||
uint16_t length = htons(pkt->length);
|
uint16_t length = htons(pkt->length);
|
||||||
|
|
||||||
@@ -1248,8 +1313,9 @@ static void dmxt_handler( u8_t *data, int len) {
|
|||||||
|
|
||||||
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
xSemaphoreTake(displayer.mutex, portMAX_DELAY);
|
||||||
|
|
||||||
// PLACEHOLDER
|
led_vu_data(data + sizeof(struct ledd_packet), offset, length);
|
||||||
//led_vu_data(data + sizeof(struct dmxt_packet), offset, length);
|
|
||||||
|
displayer.wake = 1000; // wait a little while
|
||||||
|
|
||||||
xSemaphoreGive(displayer.mutex);
|
xSemaphoreGive(displayer.mutex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
idf_component_register(SRC_DIRS .
|
idf_component_register(SRC_DIRS .
|
||||||
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets
|
PRIV_REQUIRES _override esp_common wifi-manager pthread squeezelite-ota platform_console telnet display targets led_strip
|
||||||
EMBED_FILES ../server_certs/github.pem
|
EMBED_FILES ../server_certs/github.pem
|
||||||
LDFRAGMENTS "linker.lf"
|
LDFRAGMENTS "linker.lf"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "gds_draw.h"
|
#include "gds_draw.h"
|
||||||
#include "gds_text.h"
|
#include "gds_text.h"
|
||||||
#include "gds_font.h"
|
#include "gds_font.h"
|
||||||
|
#include "led_vu.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "accessors.h"
|
#include "accessors.h"
|
||||||
#include "cmd_system.h"
|
#include "cmd_system.h"
|
||||||
@@ -73,6 +74,7 @@ extern const uint8_t server_cert_pem_end[] asm("_binary_github_pem_end");
|
|||||||
// as an exception _init function don't need include
|
// as an exception _init function don't need include
|
||||||
extern void services_init(void);
|
extern void services_init(void);
|
||||||
extern void display_init(char *welcome);
|
extern void display_init(char *welcome);
|
||||||
|
extern void led_vu_init(void);
|
||||||
extern void target_init(char *target);
|
extern void target_init(char *target);
|
||||||
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
|
const char * str_or_unknown(const char * str) { return (str?str:unknown_string_placeholder); }
|
||||||
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
|
const char * str_or_null(const char * str) { return (str?str:null_string_placeholder); }
|
||||||
@@ -368,6 +370,7 @@ void register_default_nvs(){
|
|||||||
register_default_string_val("ethtmout","8");
|
register_default_string_val("ethtmout","8");
|
||||||
register_default_string_val("dhcp_tmout","8");
|
register_default_string_val("dhcp_tmout","8");
|
||||||
register_default_string_val("target", CONFIG_TARGET);
|
register_default_string_val("target", CONFIG_TARGET);
|
||||||
|
register_default_string_val("led_vu_config", "");
|
||||||
#ifdef CONFIG_CSPOT_SINK
|
#ifdef CONFIG_CSPOT_SINK
|
||||||
register_default_string_val("enable_cspot", STR(CONFIG_CSPOT_SINK));
|
register_default_string_val("enable_cspot", STR(CONFIG_CSPOT_SINK));
|
||||||
register_default_string_val("cspot_config", "");
|
register_default_string_val("cspot_config", "");
|
||||||
@@ -467,10 +470,18 @@ void app_main()
|
|||||||
target_init(target);
|
target_init(target);
|
||||||
free(target);
|
free(target);
|
||||||
}
|
}
|
||||||
if(is_recovery_running && display){
|
ESP_LOGI(TAG,"Initializing led_vu");
|
||||||
GDS_ClearExt(display, true);
|
led_vu_init();
|
||||||
GDS_SetFont(display, &Font_line_2 );
|
|
||||||
GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY");
|
if(is_recovery_running) {
|
||||||
|
if (display) {
|
||||||
|
GDS_ClearExt(display, true);
|
||||||
|
GDS_SetFont(display, &Font_line_2 );
|
||||||
|
GDS_TextPos(display, GDS_FONT_DEFAULT, GDS_TEXT_CENTERED, GDS_TEXT_CLEAR | GDS_TEXT_UPDATE, "RECOVERY");
|
||||||
|
}
|
||||||
|
if(led_display) {
|
||||||
|
led_vu_color_yellow(LED_VU_BRIGHT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -106,6 +106,34 @@
|
|||||||
<hr>
|
<hr>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|
||||||
|
[% IF prefs.pref_led_config %]
|
||||||
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_CONFIG" desc="PLUGIN_SQUEEZEESP32_LED_CONFIG_DESC" %]
|
||||||
|
<!--<input type="text" readonly class="stdedit" name="pref_led_config" id="led_config" value="[% prefs.pref_led_config %]" size="3">-->
|
||||||
|
<input type="hidden" name="pref_led_config" value="[% prefs.pref_led_config %]">
|
||||||
|
[% prefs.pref_led_config %]
|
||||||
|
[% END %]
|
||||||
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_VISUALIZER" desc="PLUGIN_SQUEEZEESP32_LED_VISUALIZER_DESC" %]
|
||||||
|
<select class="stdedit" name="pref_led_visualizer" id="led_visualizer">
|
||||||
|
[% num = 1 %][% last = 1 %]
|
||||||
|
[% FOREACH option = ledVisualModes.keys.nsort %]
|
||||||
|
<option [% IF prefs.pref_led_visualizer == option %]selected [% ELSIF num == 1 && option == '-1' %]selected [% END %]value="[% option %]">[% ledVisualModes.$option %]</option>
|
||||||
|
[%- END -%]
|
||||||
|
</select>
|
||||||
|
[% END %]
|
||||||
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS" desc="PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS_DESC" %]
|
||||||
|
<input type="text" class="stdedit sliderInput_0_255" name="pref_led_brightness" id="led_brightness" value="[% prefs.pref_led_brightness %]" size="2">
|
||||||
|
[% END %]
|
||||||
|
|
||||||
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_LED_DATA" desc="PLUGIN_SQUEEZEESP32_LED_DATA_DESC" %]
|
||||||
|
<input type="button" name="led_data_send" onclick="SqueezeJS.Controller.request({params: ['[% playerid %]', ['dmx', document.getElementById('led_data_cmd').value, document.getElementById('led_data_x').value]] });" value="[% "PLUGIN_SQUEEZEESP32_LED_DATA_SEND" | string %]">
|
||||||
|
[% "PLUGIN_SQUEEZEESP32_LED_DATA_X" | string %] 
|
||||||
|
<input type="text" class="stdedit" name="pref_led_data_x" id="led_data_x" value="0" size="2">
|
||||||
|
[% "PLUGIN_SQUEEZEESP32_LED_DATA_CMD" | string %] 
|
||||||
|
<input type="text" class="stdedit" name="pref_led_data_cmd" id="led_data_cmd" value="80,0,0,0,80,0,0,0,80" size="50">
|
||||||
|
[% END %]
|
||||||
|
<hr>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
[% IF pref_equalizer %]
|
[% IF pref_equalizer %]
|
||||||
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_EQUALIZER" desc="" %]
|
[% WRAPPER setting title="PLUGIN_SQUEEZEESP32_EQUALIZER" desc="" %]
|
||||||
<div>[% "PLUGIN_SQUEEZEESP32_EQUALIZER_SAVE" | string %]</div>
|
<div>[% "PLUGIN_SQUEEZEESP32_EQUALIZER_SAVE" | string %]</div>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use Slim::Utils::Log;
|
|||||||
use Slim::Utils::Prefs;
|
use Slim::Utils::Prefs;
|
||||||
|
|
||||||
use Plugins::SqueezeESP32::FirmwareHelper;
|
use Plugins::SqueezeESP32::FirmwareHelper;
|
||||||
|
use Plugins::SqueezeESP32::RgbLed;
|
||||||
|
|
||||||
my $sprefs = preferences('server');
|
my $sprefs = preferences('server');
|
||||||
my $prefs = preferences('plugin.squeezeesp32');
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
@@ -63,6 +64,10 @@ sub maxTreble { 20 }
|
|||||||
sub minTreble { -13 }
|
sub minTreble { -13 }
|
||||||
sub maxBass { 20 }
|
sub maxBass { 20 }
|
||||||
sub minBass { -13 }
|
sub minBass { -13 }
|
||||||
|
sub hasLED {
|
||||||
|
my $client = shift;
|
||||||
|
return $prefs->client($client)->get('led_config') || 0;
|
||||||
|
}
|
||||||
|
|
||||||
sub init {
|
sub init {
|
||||||
my $client = shift;
|
my $client = shift;
|
||||||
@@ -98,6 +103,7 @@ sub init {
|
|||||||
|
|
||||||
$client->SUPER::init(@_);
|
$client->SUPER::init(@_);
|
||||||
Plugins::SqueezeESP32::FirmwareHelper::init($client);
|
Plugins::SqueezeESP32::FirmwareHelper::init($client);
|
||||||
|
Plugins::SqueezeESP32::RgbLed::init($client);
|
||||||
|
|
||||||
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
main::INFOLOG && $log->is_info && $log->info("SqueezeESP player connected: " . $client->id);
|
||||||
}
|
}
|
||||||
@@ -110,6 +116,9 @@ sub initPrefs {
|
|||||||
$prefs->client($client)->init( {
|
$prefs->client($client)->init( {
|
||||||
equalizer => [(0) x 10],
|
equalizer => [(0) x 10],
|
||||||
artwork => undef,
|
artwork => undef,
|
||||||
|
led_config => 0,
|
||||||
|
led_visualizer => 0,
|
||||||
|
led_brightness => 20,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
$prefs->setValidate({
|
$prefs->setValidate({
|
||||||
@@ -169,6 +178,9 @@ sub playerSettingsFrame {
|
|||||||
|
|
||||||
main::INFOLOG && $log->is_info && $log->info("Setting player $value" . "x" . "$height for ", $client->name);
|
main::INFOLOG && $log->is_info && $log->info("Setting player $value" . "x" . "$height for ", $client->name);
|
||||||
}
|
}
|
||||||
|
my $led_config = (unpack('Cnnn',$$data_ref))[3];
|
||||||
|
$prefs->client($client)->set('led_config', $led_config);
|
||||||
|
main::INFOLOG && $log->is_info && $led_config && $log->info("Setting led length $led_config for ", $client->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
$client->SUPER::playerSettingsFrame($data_ref);
|
$client->SUPER::playerSettingsFrame($data_ref);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use List::Util qw(first min max);
|
|||||||
|
|
||||||
use Slim::Utils::Log;
|
use Slim::Utils::Log;
|
||||||
use Slim::Utils::Prefs;
|
use Slim::Utils::Prefs;
|
||||||
|
use Slim::Utils::Strings qw(string cstring);
|
||||||
|
|
||||||
my $sprefs = preferences('server');
|
my $sprefs = preferences('server');
|
||||||
my $prefs = preferences('plugin.squeezeesp32');
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
@@ -33,6 +34,7 @@ sub prefs {
|
|||||||
my ($class, $client) = @_;
|
my ($class, $client) = @_;
|
||||||
my @prefs;
|
my @prefs;
|
||||||
push @prefs, qw(width small_VU) if $client->displayWidth;
|
push @prefs, qw(width small_VU) if $client->displayWidth;
|
||||||
|
push @prefs, qw(led_config led_visualizer led_brightness);# if $client->hasLED;
|
||||||
return ($prefs->client($client), @prefs);
|
return ($prefs->client($client), @prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +88,12 @@ sub handler {
|
|||||||
$cprefs->set('equalizer', $equalizer);
|
$cprefs->set('equalizer', $equalizer);
|
||||||
$client->update_tones($equalizer);
|
$client->update_tones($equalizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($client->hasLED) {
|
||||||
|
$cprefs->set('led_visualizer', $paramRef->{'pref_led_visualizer'} || 0);
|
||||||
|
$cprefs->set('led_brightness', $paramRef->{'pref_led_brightness'} || 20);
|
||||||
|
Plugins::SqueezeESP32::RgbLed::updateLED($client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($client->displayWidth) {
|
if ($client->displayWidth) {
|
||||||
@@ -95,6 +103,10 @@ sub handler {
|
|||||||
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
$paramRef->{'pref_artwork'} = $cprefs->get('artwork');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($client->hasLED) {
|
||||||
|
$paramRef->{'ledVisualModes'} = Plugins::SqueezeESP32::RgbLed::ledVisualModeOptions($client);
|
||||||
|
}
|
||||||
|
|
||||||
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->can('depth') && $client->depth == 16;
|
$paramRef->{'pref_equalizer'} = $cprefs->get('equalizer') if $client->can('depth') && $client->depth == 16;
|
||||||
$paramRef->{'player_ip'} = $client->ip;
|
$paramRef->{'player_ip'} = $client->ip;
|
||||||
|
|
||||||
|
|||||||
188
plugin/SqueezeESP32/RgbLed.pm
Normal file
188
plugin/SqueezeESP32/RgbLed.pm
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
package Plugins::SqueezeESP32::RgbLed;
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
Plugins::SqueezeESP32::RgbLed
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
L<Plugins::SqueezeESP32::RgbLed>
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use Slim::Utils::Strings qw(string cstring);
|
||||||
|
|
||||||
|
use Slim::Utils::Log;
|
||||||
|
use Slim::Utils::Prefs;
|
||||||
|
use Plugins::SqueezeESP32::Player
|
||||||
|
|
||||||
|
my $log = logger('player.RgbLed');
|
||||||
|
|
||||||
|
my $prefs = preferences('plugin.squeezeesp32');
|
||||||
|
my $log = logger('plugin.squeezeesp32');
|
||||||
|
|
||||||
|
sub init {
|
||||||
|
Slim::Control::Request::subscribe( sub { onNotification(@_) }, [ ['playlist'], ['open', 'pause', 'resume', 'stop', 'clear'] ]);
|
||||||
|
|
||||||
|
# register led visualizer comands to allow independant update and command line controls.
|
||||||
|
Slim::Control::Request::addDispatch([ 'dmx', '_data', '_xoff'], [1, 0, 0, \&sendDMX]);
|
||||||
|
Slim::Control::Request::addDispatch([ 'led_visual', '_mode', '_bright'], [1, 0, 0, \&setLEDVisu]);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $VISUALIZER_NONE = 0;
|
||||||
|
my $VISUALIZER_VUMETER = 1;
|
||||||
|
my $VISUALIZER_SPECTRUM_ANALYZER = 2;
|
||||||
|
my $VISUALIZER_WAVEFORM = 3;
|
||||||
|
my @ledvisualizers = (
|
||||||
|
{ desc => ['BLANK'],
|
||||||
|
params => [$VISUALIZER_NONE],
|
||||||
|
},
|
||||||
|
{ desc => ['VISUALIZER_ANALOG_VUMETER'],
|
||||||
|
params => [$VISUALIZER_VUMETER, 0],
|
||||||
|
},
|
||||||
|
{ desc => ['VISUALIZER_DIGITAL_VUMETER'],
|
||||||
|
params => [$VISUALIZER_VUMETER, 1],
|
||||||
|
},
|
||||||
|
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER'],
|
||||||
|
params => [$VISUALIZER_SPECTRUM_ANALYZER, 0],
|
||||||
|
},
|
||||||
|
{ desc => ['VISUALIZER_SPECTRUM_ANALYZER','2'],
|
||||||
|
params => [$VISUALIZER_SPECTRUM_ANALYZER, 1],
|
||||||
|
},
|
||||||
|
{ desc => ['PLUGIN_SQUEEZEESP32_WAVEFORM'],
|
||||||
|
params => [$VISUALIZER_WAVEFORM, 0],
|
||||||
|
},
|
||||||
|
{ desc => ['PLUGIN_SQUEEZEESP32_WAVEFORM','2'],
|
||||||
|
params => [$VISUALIZER_WAVEFORM, 1],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
my $nledvisualizers = $#ledvisualizers;
|
||||||
|
|
||||||
|
sub ledVisualizerModes {
|
||||||
|
return \@ledvisualizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ledVisualizerNModes {
|
||||||
|
return $nledvisualizers;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub updateLED {
|
||||||
|
my $client = shift;
|
||||||
|
my $cprefs = $prefs->client($client);
|
||||||
|
|
||||||
|
my $visu = $cprefs->get('led_visualizer') || 0;
|
||||||
|
my $bright = $cprefs->get('led_brightness') || 20;
|
||||||
|
|
||||||
|
$visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes || !(Slim::Player::Source::playmode($client) eq 'play'));
|
||||||
|
my $modes = ledVisualizerModes;
|
||||||
|
my $params = $modes->[$visu]{'params'};
|
||||||
|
my $data = pack('CCC', $params->[0], $params->[1], $bright);
|
||||||
|
main::INFOLOG && $log->is_debug && $log->info("Sending visu mode $visu ", $client->name);
|
||||||
|
|
||||||
|
$client->sendFrame( ledv => \$data );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ledVisualParams {
|
||||||
|
my $client = shift;
|
||||||
|
|
||||||
|
my $visu = $prefs->client($client)->get('led_visualizer') || 0;
|
||||||
|
|
||||||
|
return $ledvisualizers[$visu]{params};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ledVisualModeOptions {
|
||||||
|
my $client = shift;
|
||||||
|
|
||||||
|
my $display = {
|
||||||
|
'-1' => ' '
|
||||||
|
};
|
||||||
|
|
||||||
|
my $modes = ledVisualizerModes;
|
||||||
|
my $nmodes = ledVisualizerNModes;
|
||||||
|
|
||||||
|
for (my $i = 0; $i <= $nmodes; $i++) {
|
||||||
|
|
||||||
|
my $desc = $modes->[$i]{'desc'};
|
||||||
|
|
||||||
|
for (my $j = 0; $j < scalar @$desc; $j++) {
|
||||||
|
|
||||||
|
$display->{$i} .= ' ' if ($j > 0);
|
||||||
|
$display->{$i} .= string(@{$desc}[$j]) || @{$desc}[$j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $display;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub sendDMX {
|
||||||
|
my $request = shift;
|
||||||
|
|
||||||
|
# check this is the correct command.
|
||||||
|
if ($request->isNotCommand([['dmx']])) {
|
||||||
|
$request->setStatusBadDispatch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
# get our parameters
|
||||||
|
my $client = $request->client();
|
||||||
|
|
||||||
|
my $count = 0;
|
||||||
|
my $outData;
|
||||||
|
my @values = split(',', $request->getParam('_data') || '');
|
||||||
|
foreach my $val (@values) {
|
||||||
|
$outData .= pack ( 'C', $val);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
$count /= 3;
|
||||||
|
|
||||||
|
my $data = pack('nn', $request->getParam('_xoff') || 0, $count ) . $outData;
|
||||||
|
|
||||||
|
# changed from dmxt to ledd (matches 'ledc' for tricolor led in receiver player)
|
||||||
|
$client->sendFrame( ledd => \$data );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub setLEDVisu {
|
||||||
|
my $request = shift;
|
||||||
|
|
||||||
|
# check this is the correct command.
|
||||||
|
if ($request->isNotCommand([['led_visual']])) {
|
||||||
|
$request->setStatusBadDispatch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $client = $request->client();
|
||||||
|
return if (!$client->hasLED);
|
||||||
|
|
||||||
|
my $cprefs = $prefs->client($client);
|
||||||
|
|
||||||
|
my $visu = $cprefs->get('led_visualizer') || 0;
|
||||||
|
my $mode = $request->getParam('_mode') || -1;
|
||||||
|
if ($mode == -1) {
|
||||||
|
$visu+=1;
|
||||||
|
} else {
|
||||||
|
$visu = $mode;
|
||||||
|
}
|
||||||
|
$visu = 0 if ($visu < 0 || $visu > ledVisualizerNModes);
|
||||||
|
$cprefs->set('led_visualizer', $visu);
|
||||||
|
|
||||||
|
my $bright = $request->getParam('_bright') || -1;
|
||||||
|
if ($bright >= 0 && $bright < 256) {
|
||||||
|
$cprefs->set('led_brightness', $bright);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLED($client);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub onNotification {
|
||||||
|
my $request = shift;
|
||||||
|
my $client = $request->client || return;
|
||||||
|
|
||||||
|
foreach my $player ($client->syncGroupActiveMembers) {
|
||||||
|
next unless $player->isa('Plugins::SqueezeESP32::Player');
|
||||||
|
updateLED($player) if $player->hasLED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -106,6 +106,44 @@ PLUGIN_SQUEEZEESP32_ARTWORK_X
|
|||||||
PLUGIN_SQUEEZEESP32_ARTWORK_Y
|
PLUGIN_SQUEEZEESP32_ARTWORK_Y
|
||||||
EN Y
|
EN Y
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_CONFIG
|
||||||
|
EN Led RGB Strip
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_CONFIG_DESC
|
||||||
|
EN Length of the Led strip reported by the player
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_VISUALIZER
|
||||||
|
EN Led Visualizer
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_VISUALIZER_DESC
|
||||||
|
EN Select Led Visualizer from the built in effects
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS
|
||||||
|
EN Led Brghtness
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_BRIGHTNESS_DESC
|
||||||
|
EN Sets the brightness of the Led Visualizer effects
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_DATA
|
||||||
|
EN Led Test
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_DATA_DESC
|
||||||
|
EN Sends custom RGB data to the Led Strip.
|
||||||
|
EN <br>Enter R,G,B values (comma delimited). Repeat RGB for multiple Led sequences. Use the Offset to specifiy the first LED of the sequence.
|
||||||
|
EN <br>Use the Set button the transmit.
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_DATA_SEND
|
||||||
|
EN Set
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_DATA_X
|
||||||
|
EN Offset
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_LED_DATA_CMD
|
||||||
|
EN Command
|
||||||
|
|
||||||
|
PLUGIN_SQUEEZEESP32_WAVEFORM
|
||||||
|
EN Waveform Visualizer
|
||||||
|
|
||||||
PLUGIN_SQUEEZEESP32_EQUALIZER
|
PLUGIN_SQUEEZEESP32_EQUALIZER
|
||||||
DE Grafischer Equalizer
|
DE Grafischer Equalizer
|
||||||
EN Graphic equalizer
|
EN Graphic equalizer
|
||||||
|
|||||||
Reference in New Issue
Block a user