generated from sirlilpanda/kicad-project-template-actionless
Compare commits
8 Commits
PCB_ROUTIN
...
52aac06b2c
| Author | SHA1 | Date | |
|---|---|---|---|
| 52aac06b2c | |||
| f21f909a71 | |||
| 7d752f2534 | |||
| 012741879d | |||
| c998778acd | |||
| 91b00cb30a | |||
| e14f685a29 | |||
| 2dcff7350a |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -41,3 +41,8 @@ fp-info-cache
|
|||||||
|
|
||||||
# Local project settings
|
# Local project settings
|
||||||
*.kicad_prl
|
*.kicad_prl
|
||||||
|
|
||||||
|
# for esp idf
|
||||||
|
|
||||||
|
**/build/
|
||||||
|
**/.cache/
|
||||||
@@ -7,7 +7,7 @@ this template will auto:
|
|||||||
- create BOM for each project
|
- create BOM for each project
|
||||||
- create images of the current pcb for your readme like you can see below
|
- create images of the current pcb for your readme like you can see below
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
the code for creating all of this lives in `.hooks/`
|
the code for creating all of this lives in `.hooks/`
|
||||||
|
|
||||||
|
|||||||
8
software/blink/CMakeLists.txt
Normal file
8
software/blink/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# The following five lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(blink)
|
||||||
69
software/blink/README.md
Normal file
69
software/blink/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- |
|
||||||
|
|
||||||
|
# Blink Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This example demonstrates how to blink a LED by using the GPIO driver or using the [led_strip](https://components.espressif.com/component/espressif/led_strip) library if the LED is addressable e.g. [WS2812](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf). The `led_strip` library is installed via [component manager](main/idf_component.yml).
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with normal LED or addressable LED on-board (e.g., ESP32-S3-DevKitC, ESP32-C6-DevKitC etc.)
|
||||||
|
* A USB cable for Power supply and programming
|
||||||
|
|
||||||
|
See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it.
|
||||||
|
|
||||||
|
### Configure the Project
|
||||||
|
|
||||||
|
Open the project configuration menu (`idf.py menuconfig`).
|
||||||
|
|
||||||
|
In the `Example Configuration` menu:
|
||||||
|
|
||||||
|
* Select the LED type in the `Blink LED type` option.
|
||||||
|
* Use `GPIO` for regular LED
|
||||||
|
* Use `LED strip` for addressable LED
|
||||||
|
* If the LED type is `LED strip`, select the backend peripheral
|
||||||
|
* `RMT` is only available for ESP targets with RMT peripheral supported
|
||||||
|
* `SPI` is available for all ESP targets
|
||||||
|
* Set the GPIO number used for the signal in the `Blink GPIO number` option.
|
||||||
|
* Set the blinking period in the `Blink period in ms` option.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
As you run the example, you will see the LED blinking, according to the previously defined period. For the addressable LED, you can also change the LED color by setting the `led_strip_set_pixel(led_strip, 0, 16, 16, 16);` (LED Strip, Pixel Number, Red, Green, Blue) with values from 0 to 255 in the [source file](main/blink_example_main.c).
|
||||||
|
|
||||||
|
```text
|
||||||
|
I (315) example: Example configured to blink addressable LED!
|
||||||
|
I (325) example: Turning the LED OFF!
|
||||||
|
I (1325) example: Turning the LED ON!
|
||||||
|
I (2325) example: Turning the LED OFF!
|
||||||
|
I (3325) example: Turning the LED ON!
|
||||||
|
I (4325) example: Turning the LED OFF!
|
||||||
|
I (5325) example: Turning the LED ON!
|
||||||
|
I (6325) example: Turning the LED OFF!
|
||||||
|
I (7325) example: Turning the LED ON!
|
||||||
|
I (8325) example: Turning the LED OFF!
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: The color order could be different according to the LED model.
|
||||||
|
|
||||||
|
The pixel number indicates the pixel position in the LED strip. For a single LED, use 0.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
* If the LED isn't blinking, check the GPIO or the LED type selection in the `Example Configuration` menu.
|
||||||
|
|
||||||
|
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
||||||
20
software/blink/dependencies.lock
Normal file
20
software/blink/dependencies.lock
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
component_hash: 28621486f77229aaf81c71f5e15d6fbf36c2949cf11094e07090593e659e7639
|
||||||
|
dependencies:
|
||||||
|
- name: idf
|
||||||
|
require: private
|
||||||
|
version: '>=5.0'
|
||||||
|
source:
|
||||||
|
registry_url: https://components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 3.0.3
|
||||||
|
idf:
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 6.1.0
|
||||||
|
direct_dependencies:
|
||||||
|
- espressif/led_strip
|
||||||
|
manifest_hash: cdcc77aff1f56b906c61e51c8f438577513c8d92aa9dfd7bf655f126d2b2d320
|
||||||
|
target: esp32s3
|
||||||
|
version: 3.0.0
|
||||||
2
software/blink/main/CMakeLists.txt
Normal file
2
software/blink/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "blink_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
49
software/blink/main/Kconfig.projbuild
Normal file
49
software/blink/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
|
||||||
|
|
||||||
|
choice BLINK_LED
|
||||||
|
prompt "Blink LED type"
|
||||||
|
default BLINK_LED_GPIO
|
||||||
|
help
|
||||||
|
Select the LED type. A normal level controlled LED or an addressable LED strip.
|
||||||
|
The default selection is based on the Espressif DevKit boards.
|
||||||
|
You can change the default selection according to your board.
|
||||||
|
|
||||||
|
config BLINK_LED_GPIO
|
||||||
|
bool "GPIO"
|
||||||
|
config BLINK_LED_STRIP
|
||||||
|
bool "LED strip"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
choice BLINK_LED_STRIP_BACKEND
|
||||||
|
depends on BLINK_LED_STRIP
|
||||||
|
prompt "LED strip backend peripheral"
|
||||||
|
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
|
||||||
|
default BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
help
|
||||||
|
Select the backend peripheral to drive the LED strip.
|
||||||
|
|
||||||
|
config BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
depends on SOC_RMT_SUPPORTED
|
||||||
|
bool "RMT"
|
||||||
|
config BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
bool "SPI"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config BLINK_GPIO
|
||||||
|
int "Blink GPIO number"
|
||||||
|
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
|
||||||
|
default 8
|
||||||
|
help
|
||||||
|
GPIO number (IOxx) to blink on and off the LED.
|
||||||
|
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
|
||||||
|
|
||||||
|
config BLINK_PERIOD
|
||||||
|
int "Blink period in ms"
|
||||||
|
range 10 3600000
|
||||||
|
default 1000
|
||||||
|
help
|
||||||
|
Define the blinking period in milliseconds.
|
||||||
|
|
||||||
|
endmenu
|
||||||
108
software/blink/main/blink_example_main.c
Normal file
108
software/blink/main/blink_example_main.c
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
/* Blink Example
|
||||||
|
|
||||||
|
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, this
|
||||||
|
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink,
|
||||||
|
or you can edit the following line and set a number here.
|
||||||
|
*/
|
||||||
|
#define BLINK_GPIO 38
|
||||||
|
|
||||||
|
static uint8_t s_led_state = 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLINK_LED_STRIP
|
||||||
|
|
||||||
|
static led_strip_handle_t led_strip;
|
||||||
|
|
||||||
|
static void blink_led(void)
|
||||||
|
{
|
||||||
|
/* If the addressable LED is enabled */
|
||||||
|
if (s_led_state) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
led_strip_set_pixel(led_strip, 0, 255, 0, 0);
|
||||||
|
led_strip_set_pixel(led_strip, 1, 255, 128, 0);
|
||||||
|
led_strip_set_pixel(led_strip, 2, 255, 255, 0);
|
||||||
|
led_strip_set_pixel(led_strip, 3, 0, 255, 0);
|
||||||
|
led_strip_set_pixel(led_strip, 4, 0, 0, 255);
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
led_strip_refresh(led_strip);
|
||||||
|
} else {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_strip_clear(led_strip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void configure_led(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Example configured to blink addressable LED!");
|
||||||
|
/* LED strip initialization with the GPIO and pixels number*/
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = BLINK_GPIO,
|
||||||
|
.max_leds = 5, // at least one LED on board
|
||||||
|
};
|
||||||
|
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, // 10MHz
|
||||||
|
.flags.with_dma = false,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.spi_bus = SPI2_HOST,
|
||||||
|
.flags.with_dma = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
#else
|
||||||
|
#error "unsupported LED strip backend"
|
||||||
|
#endif
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
led_strip_clear(led_strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif CONFIG_BLINK_LED_GPIO
|
||||||
|
|
||||||
|
static void blink_led(void)
|
||||||
|
{
|
||||||
|
/* Set the GPIO level according to the state (LOW or HIGH)*/
|
||||||
|
gpio_set_level(BLINK_GPIO, s_led_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void configure_led(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
|
||||||
|
gpio_reset_pin(BLINK_GPIO);
|
||||||
|
/* Set the GPIO as a push/pull output */
|
||||||
|
gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "unsupported LED type"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Configure the peripheral according to the LED type */
|
||||||
|
configure_led();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ESP_LOGI(TAG, "Turning the LED %s!", s_led_state == true ? "ON" : "OFF");
|
||||||
|
blink_led();
|
||||||
|
/* Toggle the LED state */
|
||||||
|
s_led_state = !s_led_state;
|
||||||
|
vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
software/blink/main/idf_component.yml
Normal file
2
software/blink/main/idf_component.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^3.0.0"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
28621486f77229aaf81c71f5e15d6fbf36c2949cf11094e07090593e659e7639
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
## 3.0.3
|
||||||
|
|
||||||
|
- Support WS2816 with 16-bit color
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
- Support WS2811 bit timing
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
- Discontinued support for ESP-IDF v4.x
|
||||||
|
- Added configuration for user-defined color component format
|
||||||
|
|
||||||
|
## 2.5.5
|
||||||
|
|
||||||
|
- Simplified the led_strip component dependency, the time of full build with ESP-IDF v5.3 can now be shorter.
|
||||||
|
|
||||||
|
## 2.5.4
|
||||||
|
|
||||||
|
- Inserted extra delay when initialize the SPI LED device, to ensure all LEDs are in the reset state correctly
|
||||||
|
|
||||||
|
## 2.5.3
|
||||||
|
|
||||||
|
- Extend reset time (280us) to support WS2812B-V5
|
||||||
|
|
||||||
|
## 2.5.2
|
||||||
|
|
||||||
|
- Added API reference doc (api.md)
|
||||||
|
|
||||||
|
## 2.5.0
|
||||||
|
|
||||||
|
- Enabled support for IDF4.4 and above
|
||||||
|
- with RMT backend only
|
||||||
|
- Added API `led_strip_set_pixel_hsv`
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Support configurable SPI mode to control leds
|
||||||
|
- recommend enabling DMA when using SPI mode
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
- Support configurable RMT channel size by setting `mem_block_symbols`
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
|
||||||
|
- Support for 4 components RGBW leds (SK6812):
|
||||||
|
- in led_strip_config_t new fields
|
||||||
|
led_pixel_format, controlling byte format (LED_PIXEL_FORMAT_GRB, LED_PIXEL_FORMAT_GRBW)
|
||||||
|
led_model, used to configure bit timing (LED_MODEL_WS2812, LED_MODEL_SK6812)
|
||||||
|
- new API led_strip_set_pixel_rgbw
|
||||||
|
- new interface type set_pixel_rgbw
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs
|
||||||
|
- Support various RMT clock sources
|
||||||
|
- Acquire and release the power management lock before and after each refresh
|
||||||
|
- New driver flag: `invert_out` which can invert the led control signal by hardware
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`)
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":"1.0","algorithm":"sha256","created_at":"2026-02-04T21:14:01.842522+00:00","files":[{"path":"CHANGELOG.md","size":1667,"hash":"b445b45b8ce496848e247b569090efc3ea1a8680b8e69a7309dbdeeb97eb9d51"},{"path":"CMakeLists.txt","size":917,"hash":"038cbe6ba04c27101892e51d9d6a0627d64130f666f5d61b1f097462f982955b"},{"path":"LICENSE","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"README.md","size":2072,"hash":"12e83a316c51d85c6c1ee2e5eecfb46691f6be42ce685eece2ce063a9c949001"},{"path":"idf_component.yml","size":492,"hash":"f4f09e02e4e53be5b49defeab1b6da6cdb9acfe377df892dfef5a0c370a7f88e"},{"path":"docs/Doxyfile","size":738,"hash":"7f64bdef18c3ed6f2e3d6397066e2fad4b5e31c2052744ca9631f34f69fdff79"},{"path":"docs/book.toml","size":297,"hash":"5d66624796168a4b8d0d87631c438c392b973206f4f7c53d9897a0b7ca7ce5b4"},{"path":"include/led_strip.h","size":4313,"hash":"36344aae936d7e0e764954188d77160c357d3ae1990ede1c7451e2bd1c0e85eb"},{"path":"include/led_strip_rmt.h","size":1630,"hash":"c63a152ab4aa187080b8d29cdb49365a9ea03b6ca7c41c66920e5c58ac0d0c52"},{"path":"include/led_strip_spi.h","size":1599,"hash":"cf0dcd5c748a7f11bf55077325b68a64ea826e55fc8e7b38aaad6fc0eb5345e5"},{"path":"include/led_strip_types.h","size":4276,"hash":"168b30b21ecbc5789903204753958c781304324e6a44d40a5fcb8676f12cb29c"},{"path":"interface/led_strip_interface.h","size":2934,"hash":"5b7d0c326d0d0d9748830d4aec46d765400e1446055d4a1197c83111e937d74c"},{"path":"src/led_strip_api.c","size":3841,"hash":"61968f0dde1cf1720f6b00bf3cb5d2c2b990aac6de00c90d9465746afc3e03d5"},{"path":"src/led_strip_rmt_dev.c","size":8925,"hash":"0e69d4743e65956ef2494d1347b81b99f283e0a79d6d341733fd1d58c1c6e97e"},{"path":"src/led_strip_rmt_encoder.c","size":8080,"hash":"4ab03dfbba4a90f392af3234665f76ccd21c004c61f5575e1e428354512d1748"},{"path":"src/led_strip_rmt_encoder.h","size":977,"hash":"690381c35ace2703a5c7156f6547a8524f4cbfe5bef40be619e2097960120a40"},{"path":"src/led_strip_spi_dev.c","size":11600,"hash":"79a4e4c42185afdbdf2163bd945a6339bf09965a85fb20525b3d7b106d695ab2"},{"path":"examples/led_strip_rmt_ws2812/CMakeLists.txt","size":140,"hash":"526f16308e57fafd25d0fd79d872152a9214c28967f78aa9c94ebe9e73040940"},{"path":"examples/led_strip_rmt_ws2812/README.md","size":1200,"hash":"a5f39b31c5f7cbf548ee31b61ab22e430a6c823404c0ddb113703512bcb3ad3c"},{"path":"examples/led_strip_spi_ws2812/CMakeLists.txt","size":140,"hash":"61255dc48f295f09e84abd7895ae5767763ac3decb4b4584e38681ea877427e8"},{"path":"examples/led_strip_spi_ws2812/README.md","size":1201,"hash":"2c02a29197cd1f2d4af4c4c9cd44677e303b0e168a1773eef9fc3fdb39377d27"},{"path":"examples/led_strip_spi_ws2812/main/CMakeLists.txt","size":99,"hash":"34e7f83d26bca924c629ea2012e6f200b415d486907863fe936d94872ff739eb"},{"path":"examples/led_strip_spi_ws2812/main/idf_component.yml","size":68,"hash":"a0c6b9b94056e8459a9acb8d7828540b36b4f7fe9ced9011ea97ba23b2fc96d4"},{"path":"examples/led_strip_spi_ws2812/main/led_strip_spi_ws2812_main.c","size":2808,"hash":"ef7ee688e7e1f451879a7b238b2a7133ccf880adb6d0e551328150acf86f656d"},{"path":"examples/led_strip_rmt_ws2812/main/CMakeLists.txt","size":99,"hash":"8960b68811805d3aa40e1a7f44ddf7400c0d0731829b6d2b3b1584d8dcd3b392"},{"path":"examples/led_strip_rmt_ws2812/main/idf_component.yml","size":53,"hash":"d52c7e09ecb7a6e4946fb6e697d6d7127918d4334858973f8c7434b1d2f120f0"},{"path":"examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c","size":3253,"hash":"8835bd39d38dac8fb27c5e1298cb12ddf4c6ed430b4a2a1e061334f56d77f470"},{"path":"docs/src/SUMMARY.md","size":110,"hash":"b3a38ed25d2e5187928554682b1bd7154444e1bc1ce8183e6a3d328e720f7b61"},{"path":"docs/src/api.md","size":128,"hash":"d06c809c85c02f6ae22bd090331e1150dad89bd57034f056dbf3df0449cdc22b"},{"path":"docs/src/index.md","size":2967,"hash":"db944dabd24b1faa4d61a8f8db4f734334cefc2d1efb6d023a51fb94d1c3311f"}]}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||||
|
|
||||||
|
set(srcs "src/led_strip_api.c")
|
||||||
|
set(public_requires)
|
||||||
|
|
||||||
|
if(CONFIG_SOC_RMT_SUPPORTED)
|
||||||
|
list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# the SPI backend driver relies on some feature that was available in IDF 5.1
|
||||||
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||||
|
if(CONFIG_SOC_GPSPI_SUPPORTED)
|
||||||
|
list(APPEND srcs "src/led_strip_spi_dev.c")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Starting from esp-idf v5.3, the RMT and SPI drivers are moved to separate components
|
||||||
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||||
|
list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi")
|
||||||
|
else()
|
||||||
|
list(APPEND public_requires "driver")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "include" "interface"
|
||||||
|
REQUIRES ${public_requires})
|
||||||
202
software/blink/managed_components/espressif__led_strip/LICENSE
Normal file
202
software/blink/managed_components/espressif__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.
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# LED Strip Driver
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/led_strip)
|
||||||
|
|
||||||
|
This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line.
|
||||||
|
|
||||||
|
## Supported Backend Peripherals
|
||||||
|
|
||||||
|
The LED strip driver supports two different backend peripherals to generate the timing signals required by addressable LEDs:
|
||||||
|
|
||||||
|
### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral
|
||||||
|
|
||||||
|
This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible [^1].
|
||||||
|
|
||||||
|
### The [SPI](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) Peripheral
|
||||||
|
|
||||||
|
SPI peripheral can also be used to generate the timing required by the LED strip, in a so-called "Clock-less" mode. However this backend is not as economical as the RMT one, because it will take up the whole **bus**. You **CANNOT** connect other devices to the same SPI bus if it's been used by the led_strip, because the led_strip doesn't have the concept of "Chip Select".
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For detailed information about the LED Strip component, including API reference and user guides, please visit:
|
||||||
|
|
||||||
|
- **Programming Guide & API Reference**: [LED Strip Documentation](https://espressif.github.io/idf-extra-components/latest/led_strip/index.html)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Set this to the header file you want
|
||||||
|
INPUT = \
|
||||||
|
../include/ \
|
||||||
|
../interface/
|
||||||
|
|
||||||
|
# The output directory for the generated XML documentation
|
||||||
|
OUTPUT_DIRECTORY = doxygen_output
|
||||||
|
|
||||||
|
# Warning-related settings, it's recommended to keep them enabled
|
||||||
|
WARN_IF_UNDOC_ENUM_VAL = YES
|
||||||
|
WARN_AS_ERROR = YES
|
||||||
|
|
||||||
|
# Other common settings
|
||||||
|
FULL_PATH_NAMES = YES
|
||||||
|
STRIP_FROM_PATH = ../
|
||||||
|
STRIP_FROM_INC_PATH = ../
|
||||||
|
ENABLE_PREPROCESSING = YES
|
||||||
|
MACRO_EXPANSION = YES
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||||
|
EXPAND_ONLY_PREDEF = YES
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
PREDEFINED = $(ENV_DOXYGEN_DEFINES)
|
||||||
|
HAVE_DOT = NO
|
||||||
|
GENERATE_XML = YES
|
||||||
|
XML_OUTPUT = xml
|
||||||
|
GENERATE_HTML = NO
|
||||||
|
HAVE_DOT = NO
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
QUIET = YES
|
||||||
|
MARKDOWN_SUPPORT = YES
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[book]
|
||||||
|
title = "LED Strip Documentation"
|
||||||
|
language = "en"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
default-theme = "light"
|
||||||
|
git-repository-url = "https://github.com/espressif/idf-extra-components/tree/master/led_strip"
|
||||||
|
edit-url-template = "https://github.com/espressif/idf-extra-components/edit/master/led_strip/docs/{path}"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Programming Guide
|
||||||
|
|
||||||
|
- [LED Strip](index.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
- [API Reference](api.md)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# API Reference
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
|
||||||
|
This file is automatically generated by esp-doxybook.
|
||||||
|
|
||||||
|
DO NOT edit it manually.
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# LED Strip Programming Guide
|
||||||
|
|
||||||
|
## Allocate LED Strip Object with RMT Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
/// LED strip common configuration
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = 1, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// RMT backend specific configuration
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, // RMT counter clock frequency: 10MHz
|
||||||
|
.mem_block_symbols = 64, // the memory size of each RMT channel, in words (4 bytes)
|
||||||
|
.flags = {
|
||||||
|
.with_dma = false, // DMA feature is available on chips like ESP32-S3/P4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create the LED strip object
|
||||||
|
led_strip_handle_t led_strip = NULL;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You can create multiple LED strip objects with different GPIOs and pixel numbers. The backend driver will automatically allocate sufficient RMT channels for you wherever possible. If the RMT channels are not enough, the [led_strip_new_rmt_device](api.md#function-led_strip_new_rmt_device) will return an error.
|
||||||
|
|
||||||
|
## Allocate LED Strip Object with SPI Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
/// LED strip common configuration
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = 1, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// SPI backend specific configuration
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
.flags = {
|
||||||
|
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create the LED strip object
|
||||||
|
led_strip_handle_t led_strip = NULL;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The number of LED strip objects can be created depends on how many free SPI controllers are free to use in your project.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
- How to set the brightness of the LED strip?
|
||||||
|
- You can tune the brightness by scaling the value of each R-G-B element with a **same** factor. But pay attention to the overflow of the value.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(led_strip_rmt_ws2812)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# LED Strip Example (RMT backend + WS2812)
|
||||||
|
|
||||||
|
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with Espressif SoC
|
||||||
|
* A USB cable for Power supply and programming
|
||||||
|
* WS2812 LED strip
|
||||||
|
|
||||||
|
### Configure the Example
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_rmt_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
I (299) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||||
|
I (309) example: Created LED strip object with RMT backend
|
||||||
|
I (309) example: Start blinking LED strip
|
||||||
|
```
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "led_strip_rmt_ws2812_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: ^3
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
// Set to 1 to use DMA for driving the LED strip, 0 otherwise
|
||||||
|
// Please note the RMT DMA feature is only available on chips e.g. ESP32-S3/P4
|
||||||
|
#define LED_STRIP_USE_DMA 0
|
||||||
|
|
||||||
|
#if LED_STRIP_USE_DMA
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 256
|
||||||
|
#define LED_STRIP_MEMORY_BLOCK_WORDS 1024 // this determines the DMA block size
|
||||||
|
#else
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 24
|
||||||
|
#define LED_STRIP_MEMORY_BLOCK_WORDS 0 // let the driver choose a proper memory block size automatically
|
||||||
|
#endif // LED_STRIP_USE_DMA
|
||||||
|
|
||||||
|
// GPIO assignment
|
||||||
|
#define LED_STRIP_GPIO_PIN 2
|
||||||
|
|
||||||
|
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||||
|
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
led_strip_handle_t configure_led(void)
|
||||||
|
{
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color order of the strip: GRB
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED strip backend configuration: RMT
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
|
||||||
|
.mem_block_symbols = LED_STRIP_MEMORY_BLOCK_WORDS, // the memory block size used by the RMT channel
|
||||||
|
.flags = {
|
||||||
|
.with_dma = LED_STRIP_USE_DMA, // Using DMA can improve performance when driving more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED Strip object handle
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
ESP_LOGI(TAG, "Created LED strip object with RMT backend");
|
||||||
|
return led_strip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
led_strip_handle_t led_strip = configure_led();
|
||||||
|
bool led_on_off = false;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||||
|
while (1) {
|
||||||
|
if (led_on_off) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||||
|
}
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED ON!");
|
||||||
|
} else {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED OFF!");
|
||||||
|
}
|
||||||
|
|
||||||
|
led_on_off = !led_on_off;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(led_strip_spi_ws2812)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# LED Strip Example (SPI backend + WS2812)
|
||||||
|
|
||||||
|
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with Espressif SoC
|
||||||
|
* A USB cable for Power supply and programming
|
||||||
|
* WS2812 LED strip
|
||||||
|
|
||||||
|
### Configure the Example
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_spi_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
I (299) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||||
|
I (309) example: Created LED strip object with SPI backend
|
||||||
|
I (309) example: Start blinking LED strip
|
||||||
|
```
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "led_strip_spi_ws2812_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: ^3
|
||||||
|
idf: '>=5.1'
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
// GPIO assignment
|
||||||
|
#define LED_STRIP_GPIO_PIN 2
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 24
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
led_strip_handle_t configure_led(void)
|
||||||
|
{
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
// set the color order of the strip: GRB
|
||||||
|
.color_component_format = {
|
||||||
|
.format = {
|
||||||
|
.r_pos = 1, // red is the second byte in the color data
|
||||||
|
.g_pos = 0, // green is the first byte in the color data
|
||||||
|
.b_pos = 2, // blue is the third byte in the color data
|
||||||
|
.num_components = 3, // total 3 color components
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED strip backend configuration: SPI
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
.flags = {
|
||||||
|
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED Strip object handle
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
ESP_LOGI(TAG, "Created LED strip object with SPI backend");
|
||||||
|
return led_strip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
led_strip_handle_t led_strip = configure_led();
|
||||||
|
bool led_on_off = false;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||||
|
while (1) {
|
||||||
|
if (led_on_off) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||||
|
}
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED ON!");
|
||||||
|
} else {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED OFF!");
|
||||||
|
}
|
||||||
|
|
||||||
|
led_on_off = !led_on_off;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
dependencies:
|
||||||
|
idf: '>=5.0'
|
||||||
|
description: Driver for Addressable LED Strip (WS2812, etc)
|
||||||
|
documentation: https://espressif.github.io/idf-extra-components/latest/led_strip/index.html
|
||||||
|
issues: https://github.com/espressif/idf-extra-components/issues
|
||||||
|
repository: git://github.com/espressif/idf-extra-components.git
|
||||||
|
repository_info:
|
||||||
|
commit_sha: 7cd447361ca2f0a1c01aa3089e3031f6171b6c7e
|
||||||
|
path: led_strip
|
||||||
|
url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
|
||||||
|
version: 3.0.3
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "led_strip_rmt.h"
|
||||||
|
#include "led_strip_spi.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGB for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||||
|
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGBW for a specific pixel
|
||||||
|
*
|
||||||
|
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||||
|
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
* @param white: separate white component
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set HSV for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param hue: hue part of color (0 - 360)
|
||||||
|
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||||
|
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set HSV for a specific pixel in 16-bit resolution
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param hue: hue part of color (0 - 360)
|
||||||
|
* @param saturation: saturation part of color (0 - 65535, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 32767)
|
||||||
|
* @param value: value part of color (0 - 65535, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 32767)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Refresh memory colors to LEDs
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Refresh successfully
|
||||||
|
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||||
|
*
|
||||||
|
* @note:
|
||||||
|
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear LED strip (turn off all LEDs)
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Clear LEDs successfully
|
||||||
|
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free LED strip resources
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Free resources successfully
|
||||||
|
* - ESP_FAIL: Free resources failed because error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_del(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "led_strip_types.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#include "driver/rmt_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip RMT specific configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
rmt_clock_source_t clk_src; /*!< RMT clock source */
|
||||||
|
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
|
||||||
|
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
|
||||||
|
/*!< Extra RMT specific driver flags */
|
||||||
|
struct led_strip_rmt_extra_config {
|
||||||
|
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_rmt_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LED strip based on RMT TX channel
|
||||||
|
*
|
||||||
|
* @param led_config LED strip configuration
|
||||||
|
* @param rmt_config RMT specific configuration
|
||||||
|
* @param ret_strip Returned LED strip handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: create LED strip handle successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||||
|
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||||
|
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "led_strip_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip SPI specific configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spi_clock_source_t clk_src; /*!< SPI clock source */
|
||||||
|
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
|
||||||
|
struct {
|
||||||
|
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_spi_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LED strip based on SPI MOSI channel
|
||||||
|
*
|
||||||
|
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||||
|
*
|
||||||
|
* @param led_config LED strip configuration
|
||||||
|
* @param spi_config SPI specific configuration
|
||||||
|
* @param ret_strip Returned LED strip handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: create LED strip handle successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||||
|
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||||
|
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type of LED strip handle
|
||||||
|
*/
|
||||||
|
typedef struct led_strip_t *led_strip_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip model
|
||||||
|
* @note Different led model may have different timing parameters, so we need to distinguish them.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
|
||||||
|
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
|
||||||
|
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
|
||||||
|
LED_MODEL_WS2816, /*!< LED strip model: WS2816 */
|
||||||
|
LED_MODEL_INVALID /*!< Invalid LED strip model */
|
||||||
|
} led_model_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED color component format
|
||||||
|
* @note The format is used to specify the order of color components in each pixel, also the number of color components.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct format_layout {
|
||||||
|
uint32_t r_pos: 2; /*!< Position of the red channel in the color order: 0~3 */
|
||||||
|
uint32_t g_pos: 2; /*!< Position of the green channel in the color order: 0~3 */
|
||||||
|
uint32_t b_pos: 2; /*!< Position of the blue channel in the color order: 0~3 */
|
||||||
|
uint32_t w_pos: 2; /*!< Position of the white channel in the color order: 0~3 */
|
||||||
|
uint32_t reserved: 19; /*!< Reserved */
|
||||||
|
uint32_t bytes_per_color: 2; /*!< Bytes per color component: 1 or 2. If set to 0, it will fallback to 1 */
|
||||||
|
uint32_t num_components: 3; /*!< Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3 */
|
||||||
|
} format; /*!< Format layout */
|
||||||
|
uint32_t format_id; /*!< Format ID */
|
||||||
|
} led_color_component_format_t;
|
||||||
|
|
||||||
|
/// Helper macros to set the color component format
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip common configurations
|
||||||
|
* The common configurations are not specific to any backend peripheral.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||||
|
uint32_t max_leds; /*!< Maximum number of LEDs that can be controlled in a single strip */
|
||||||
|
led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */
|
||||||
|
led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel.
|
||||||
|
Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */
|
||||||
|
/*!< LED strip extra driver flags */
|
||||||
|
struct led_strip_extra_flags {
|
||||||
|
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_config_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip interface definition
|
||||||
|
*/
|
||||||
|
struct led_strip_t {
|
||||||
|
/**
|
||||||
|
* @brief Set RGB for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||||
|
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
* @param white: separate white component
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Refresh memory colors to LEDs
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param timeout_ms: timeout value for refreshing task
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Refresh successfully
|
||||||
|
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||||
|
*
|
||||||
|
* @note:
|
||||||
|
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||||
|
*/
|
||||||
|
esp_err_t (*refresh)(led_strip_t *strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear LED strip (turn off all LEDs)
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param timeout_ms: timeout value for clearing task
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Clear LEDs successfully
|
||||||
|
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*clear)(led_strip_t *strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free LED strip resources
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Free resources successfully
|
||||||
|
* - ESP_FAIL: Free resources failed because error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*del)(led_strip_t *strip);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "led_strip_interface.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_strip";
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
|
||||||
|
uint32_t red = 0;
|
||||||
|
uint32_t green = 0;
|
||||||
|
uint32_t blue = 0;
|
||||||
|
|
||||||
|
uint32_t rgb_max = value;
|
||||||
|
uint32_t rgb_min = rgb_max * (255 - saturation) / 255;
|
||||||
|
|
||||||
|
uint32_t i = hue / 60;
|
||||||
|
uint32_t diff = hue % 60;
|
||||||
|
|
||||||
|
// RGB adjustment amount by hue
|
||||||
|
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min + rgb_adj;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
red = rgb_max - rgb_adj;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min + rgb_adj;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max - rgb_adj;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
red = rgb_min + rgb_adj;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max - rgb_adj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
|
||||||
|
uint32_t red = 0;
|
||||||
|
uint32_t green = 0;
|
||||||
|
uint32_t blue = 0;
|
||||||
|
|
||||||
|
uint32_t rgb_max = value;
|
||||||
|
uint32_t rgb_min = rgb_max * (65535 - saturation) / 65535;
|
||||||
|
|
||||||
|
uint32_t i = hue / 60;
|
||||||
|
uint32_t diff = hue % 60;
|
||||||
|
|
||||||
|
// RGB adjustment amount by hue
|
||||||
|
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min + rgb_adj;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
red = rgb_max - rgb_adj;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min + rgb_adj;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max - rgb_adj;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
red = rgb_min + rgb_adj;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max - rgb_adj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->refresh(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->clear(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->del(strip);
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/rmt_tx.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "led_strip_interface.h"
|
||||||
|
#include "led_strip_rmt_encoder.h"
|
||||||
|
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||||
|
// the memory size of each RMT channel, in words (4 bytes)
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||||
|
#else
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "led_strip_rmt";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
led_strip_t base;
|
||||||
|
rmt_channel_handle_t rmt_chan;
|
||||||
|
rmt_encoder_handle_t strip_encoder;
|
||||||
|
uint32_t strip_len;
|
||||||
|
uint8_t bytes_per_pixel;
|
||||||
|
led_color_component_format_t component_fmt;
|
||||||
|
uint8_t pixel_buf[];
|
||||||
|
} led_strip_rmt_obj;
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
|
||||||
|
struct format_layout format = rmt_strip->component_fmt.format;
|
||||||
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||||
|
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||||
|
if (format.num_components > 3) {
|
||||||
|
pixel_buf[start + format.w_pos * pos_bytes + i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
struct format_layout format = rmt_strip->component_fmt.format;
|
||||||
|
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
ESP_RETURN_ON_FALSE(format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||||
|
|
||||||
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||||
|
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.w_pos * pos_bytes + i] = (white >> color_shift) & 0xFF;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
rmt_transmit_config_t tx_conf = {
|
||||||
|
.loop_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||||
|
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
// Write zero to turn off all leds
|
||||||
|
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||||
|
return led_strip_rmt_refresh(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||||
|
free(rmt_strip);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_new_rmt_device(const led_strip_config_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = NULL;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||||
|
// If R/G/B order is not specified, set default GRB order as fallback
|
||||||
|
if (component_fmt.format_id == 0) {
|
||||||
|
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||||
|
}
|
||||||
|
if (led_config->led_model == LED_MODEL_WS2816) {
|
||||||
|
component_fmt.format.bytes_per_color = 2;
|
||||||
|
}
|
||||||
|
if (component_fmt.format.bytes_per_color == 0) {
|
||||||
|
component_fmt.format.bytes_per_color = 1;
|
||||||
|
}
|
||||||
|
// check the validation of the color component format
|
||||||
|
uint8_t mask = 0;
|
||||||
|
if (component_fmt.format.num_components == 3) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else if (component_fmt.format.num_components == 4) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||||
|
}
|
||||||
|
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||||
|
if (component_fmt.format.bytes_per_color > 1) {
|
||||||
|
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
||||||
|
}
|
||||||
|
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||||
|
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||||
|
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||||
|
|
||||||
|
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||||
|
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||||
|
if (rmt_config->clk_src) {
|
||||||
|
clk_src = rmt_config->clk_src;
|
||||||
|
}
|
||||||
|
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||||
|
// override the default value if the user sets it
|
||||||
|
if (rmt_config->mem_block_symbols) {
|
||||||
|
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||||
|
}
|
||||||
|
rmt_tx_channel_config_t rmt_chan_config = {
|
||||||
|
.clk_src = clk_src,
|
||||||
|
.gpio_num = led_config->strip_gpio_num,
|
||||||
|
.mem_block_symbols = mem_block_symbols,
|
||||||
|
.resolution_hz = resolution,
|
||||||
|
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||||
|
.flags.with_dma = rmt_config->flags.with_dma,
|
||||||
|
.flags.invert_out = led_config->flags.invert_out,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||||
|
|
||||||
|
led_strip_encoder_config_t strip_encoder_conf = {
|
||||||
|
.resolution = resolution,
|
||||||
|
.led_model = led_config->led_model
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||||
|
|
||||||
|
rmt_strip->component_fmt = component_fmt;
|
||||||
|
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||||
|
rmt_strip->strip_len = led_config->max_leds;
|
||||||
|
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||||
|
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||||
|
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||||
|
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||||
|
rmt_strip->base.del = led_strip_rmt_del;
|
||||||
|
|
||||||
|
*ret_strip = &rmt_strip->base;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
if (rmt_strip) {
|
||||||
|
if (rmt_strip->rmt_chan) {
|
||||||
|
rmt_del_channel(rmt_strip->rmt_chan);
|
||||||
|
}
|
||||||
|
if (rmt_strip->strip_encoder) {
|
||||||
|
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||||
|
}
|
||||||
|
free(rmt_strip);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "led_strip_rmt_encoder.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_rmt_encoder";
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||||
|
#if CONFIG_RMT_ISR_IRAM_SAFE
|
||||||
|
#define RMT_ENCODER_FUNC_ATTR IRAM_ATTR
|
||||||
|
#else
|
||||||
|
#define RMT_ENCODER_FUNC_ATTR
|
||||||
|
#endif // CONFIG_RMT_ISR_IRAM_SAFE
|
||||||
|
#endif // ESP_IDF_VERSION
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rmt_encoder_t base;
|
||||||
|
rmt_encoder_t *bytes_encoder;
|
||||||
|
rmt_encoder_t *copy_encoder;
|
||||||
|
int state;
|
||||||
|
rmt_symbol_word_t reset_code;
|
||||||
|
} rmt_led_strip_encoder_t;
|
||||||
|
|
||||||
|
RMT_ENCODER_FUNC_ATTR
|
||||||
|
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||||
|
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||||
|
rmt_encode_state_t session_state = 0;
|
||||||
|
rmt_encode_state_t state = 0;
|
||||||
|
size_t encoded_symbols = 0;
|
||||||
|
switch (led_encoder->state) {
|
||||||
|
case 0: // send RGB data
|
||||||
|
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||||
|
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||||
|
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||||
|
}
|
||||||
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||||
|
state |= RMT_ENCODING_MEM_FULL;
|
||||||
|
goto out; // yield if there's no free space for encoding artifacts
|
||||||
|
}
|
||||||
|
// fall-through
|
||||||
|
case 1: // send reset code
|
||||||
|
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||||
|
sizeof(led_encoder->reset_code), &session_state);
|
||||||
|
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||||
|
led_encoder->state = 0; // back to the initial encoding session
|
||||||
|
state |= RMT_ENCODING_COMPLETE;
|
||||||
|
}
|
||||||
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||||
|
state |= RMT_ENCODING_MEM_FULL;
|
||||||
|
goto out; // yield if there's no free space for encoding artifacts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
*ret_state = state;
|
||||||
|
return encoded_symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||||
|
rmt_del_encoder(led_encoder->copy_encoder);
|
||||||
|
free(led_encoder);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RMT_ENCODER_FUNC_ATTR
|
||||||
|
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||||
|
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||||
|
led_encoder->state = 0;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||||
|
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||||
|
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||||
|
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||||
|
led_encoder->base.encode = rmt_encode_led_strip;
|
||||||
|
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||||
|
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||||
|
rmt_bytes_encoder_config_t bytes_encoder_config;
|
||||||
|
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accommodate WS2812B-V5
|
||||||
|
if (config->led_model == LED_MODEL_SK6812) {
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
|
||||||
|
};
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||||
|
};
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2811) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2811
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.5 * config->resolution / 1000000., // T0H=0.5us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 2.0 * config->resolution / 1000000., // T0L=2.0us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 1.2 * config->resolution / 1000000., // T1H=1.2us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 1.3 * config->resolution / 1000000., // T1L=1.3us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1
|
||||||
|
};
|
||||||
|
reset_ticks = config->resolution / 1000000 * 50 / 2; // divide by 2... signal is sent twice
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2816) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2816
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.95 * config->resolution / 1000000, // T0L=0.95us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.75 * config->resolution / 1000000, // T1H=0.75us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.5 * config->resolution / 1000000, // T1L=0.5us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||||
|
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||||
|
|
||||||
|
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||||
|
.level0 = 0,
|
||||||
|
.duration0 = reset_ticks,
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = reset_ticks,
|
||||||
|
};
|
||||||
|
*ret_encoder = &led_encoder->base;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
if (led_encoder) {
|
||||||
|
if (led_encoder->bytes_encoder) {
|
||||||
|
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||||
|
}
|
||||||
|
if (led_encoder->copy_encoder) {
|
||||||
|
rmt_del_encoder(led_encoder->copy_encoder);
|
||||||
|
}
|
||||||
|
free(led_encoder);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "driver/rmt_encoder.h"
|
||||||
|
#include "led_strip_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type of led strip encoder configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
uint32_t resolution; /*!< Encoder resolution, in Hz */
|
||||||
|
led_model_t led_model; /*!< LED model */
|
||||||
|
} led_strip_encoder_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create RMT encoder for encoding LED strip pixels into RMT symbols
|
||||||
|
*
|
||||||
|
* @param[in] config Encoder configuration
|
||||||
|
* @param[out] ret_encoder Returned encoder handle
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_INVALID_ARG for any invalid arguments
|
||||||
|
* - ESP_ERR_NO_MEM out of memory when creating led strip encoder
|
||||||
|
* - ESP_OK if creating encoder successfully
|
||||||
|
*/
|
||||||
|
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_rom_gpio.h"
|
||||||
|
#include "soc/spi_periph.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "led_strip_interface.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
|
||||||
|
#define LED_STRIP_SPI_DEFAULT_RESOLUTION (2.5 * 1000 * 1000) // 2.5MHz resolution
|
||||||
|
#define LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||||
|
|
||||||
|
#define SPI_BYTES_PER_COLOR_BYTE 3
|
||||||
|
#define SPI_BITS_PER_COLOR_BYTE (SPI_BYTES_PER_COLOR_BYTE * 8)
|
||||||
|
|
||||||
|
static const char *TAG = "led_strip_spi";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
led_strip_t base;
|
||||||
|
spi_host_device_t spi_host;
|
||||||
|
spi_device_handle_t spi_device;
|
||||||
|
uint32_t strip_len;
|
||||||
|
uint8_t bytes_per_pixel;
|
||||||
|
led_color_component_format_t component_fmt;
|
||||||
|
uint8_t pixel_buf[];
|
||||||
|
} led_strip_spi_obj;
|
||||||
|
|
||||||
|
// please make sure to zero-initialize the buf before calling this function
|
||||||
|
static void __led_strip_spi_bit(uint8_t data, uint8_t *buf)
|
||||||
|
{
|
||||||
|
// Each color of 1 bit is represented by 3 bits of SPI, low_level:100 ,high_level:110
|
||||||
|
// So a color byte occupies 3 bytes of SPI.
|
||||||
|
*(buf + 2) |= data & BIT(0) ? BIT(2) | BIT(1) : BIT(2);
|
||||||
|
*(buf + 2) |= data & BIT(1) ? BIT(5) | BIT(4) : BIT(5);
|
||||||
|
*(buf + 2) |= data & BIT(2) ? BIT(7) : 0x00;
|
||||||
|
*(buf + 1) |= BIT(0);
|
||||||
|
*(buf + 1) |= data & BIT(3) ? BIT(3) | BIT(2) : BIT(3);
|
||||||
|
*(buf + 1) |= data & BIT(4) ? BIT(6) | BIT(5) : BIT(6);
|
||||||
|
*(buf + 0) |= data & BIT(5) ? BIT(1) | BIT(0) : BIT(1);
|
||||||
|
*(buf + 0) |= data & BIT(6) ? BIT(4) | BIT(3) : BIT(4);
|
||||||
|
*(buf + 0) |= data & BIT(7) ? BIT(7) | BIT(6) : BIT(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_spi_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||||
|
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
// 3 pixels take 72bits(9bytes)
|
||||||
|
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||||
|
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||||
|
struct format_layout format = spi_strip->component_fmt.format;
|
||||||
|
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||||
|
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
__led_strip_spi_bit((red >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.r_pos * pos_bytes + i)]);
|
||||||
|
__led_strip_spi_bit((green >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.g_pos * pos_bytes + i)]);
|
||||||
|
__led_strip_spi_bit((blue >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.b_pos * pos_bytes + i)]);
|
||||||
|
if (format.num_components > 3) {
|
||||||
|
__led_strip_spi_bit(0, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.w_pos * pos_bytes + i)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_spi_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||||
|
struct format_layout format = spi_strip->component_fmt.format;
|
||||||
|
ESP_RETURN_ON_FALSE(index < spi_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
ESP_RETURN_ON_FALSE(format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||||
|
|
||||||
|
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||||
|
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||||
|
uint8_t *pixel_buf = spi_strip->pixel_buf;
|
||||||
|
memset(pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||||
|
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
__led_strip_spi_bit((red >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.r_pos * pos_bytes + i)]);
|
||||||
|
__led_strip_spi_bit((green >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.g_pos * pos_bytes + i)]);
|
||||||
|
__led_strip_spi_bit((blue >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.b_pos * pos_bytes + i)]);
|
||||||
|
__led_strip_spi_bit((white >> color_shift) & 0xFF, &pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * (format.w_pos * pos_bytes + i)]);
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_spi_refresh(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||||
|
spi_transaction_t tx_conf;
|
||||||
|
memset(&tx_conf, 0, sizeof(tx_conf));
|
||||||
|
|
||||||
|
tx_conf.length = spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BITS_PER_COLOR_BYTE;
|
||||||
|
tx_conf.tx_buffer = spi_strip->pixel_buf;
|
||||||
|
tx_conf.rx_buffer = NULL;
|
||||||
|
ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "transmit pixels by SPI failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_spi_clear(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||||
|
//Write zero to turn off all leds
|
||||||
|
memset(spi_strip->pixel_buf, 0, spi_strip->strip_len * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||||
|
uint8_t *buf = spi_strip->pixel_buf;
|
||||||
|
for (int index = 0; index < spi_strip->strip_len * spi_strip->bytes_per_pixel; index++) {
|
||||||
|
__led_strip_spi_bit(0, buf);
|
||||||
|
buf += SPI_BYTES_PER_COLOR_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return led_strip_spi_refresh(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_spi_del(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = __containerof(strip, led_strip_spi_obj, base);
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(spi_bus_remove_device(spi_strip->spi_device), TAG, "delete spi device failed");
|
||||||
|
ESP_RETURN_ON_ERROR(spi_bus_free(spi_strip->spi_host), TAG, "free spi bus failed");
|
||||||
|
|
||||||
|
free(spi_strip);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_new_spi_device(const led_strip_config_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip)
|
||||||
|
{
|
||||||
|
led_strip_spi_obj *spi_strip = NULL;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ESP_GOTO_ON_FALSE(led_config && spi_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||||
|
// If R/G/B order is not specified, set default GRB order as fallback
|
||||||
|
if (component_fmt.format_id == 0) {
|
||||||
|
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||||
|
}
|
||||||
|
if (led_config->led_model == LED_MODEL_WS2816) {
|
||||||
|
component_fmt.format.bytes_per_color = 2;
|
||||||
|
}
|
||||||
|
if (component_fmt.format.bytes_per_color == 0) {
|
||||||
|
component_fmt.format.bytes_per_color = 1;
|
||||||
|
}
|
||||||
|
uint8_t mask = 0;
|
||||||
|
if (component_fmt.format.num_components == 3) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else if (component_fmt.format.num_components == 4) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||||
|
}
|
||||||
|
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||||
|
if (component_fmt.format.bytes_per_color > 1) {
|
||||||
|
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
||||||
|
}
|
||||||
|
uint32_t mem_caps = MALLOC_CAP_DEFAULT;
|
||||||
|
if (spi_config->flags.with_dma) {
|
||||||
|
// DMA buffer must be placed in internal SRAM
|
||||||
|
mem_caps |= MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA;
|
||||||
|
}
|
||||||
|
spi_strip = heap_caps_calloc(1, sizeof(led_strip_spi_obj) + led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE, mem_caps);
|
||||||
|
|
||||||
|
ESP_GOTO_ON_FALSE(spi_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for spi strip");
|
||||||
|
|
||||||
|
spi_strip->spi_host = spi_config->spi_bus;
|
||||||
|
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||||
|
spi_clock_source_t clk_src = SPI_CLK_SRC_DEFAULT;
|
||||||
|
if (spi_config->clk_src) {
|
||||||
|
clk_src = spi_config->clk_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_bus_config_t spi_bus_cfg = {
|
||||||
|
.mosi_io_num = led_config->strip_gpio_num,
|
||||||
|
//Only use MOSI to generate the signal, set -1 when other pins are not used.
|
||||||
|
.miso_io_num = -1,
|
||||||
|
.sclk_io_num = -1,
|
||||||
|
.quadwp_io_num = -1,
|
||||||
|
.quadhd_io_num = -1,
|
||||||
|
.max_transfer_sz = led_config->max_leds * bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(spi_bus_initialize(spi_strip->spi_host, &spi_bus_cfg, spi_config->flags.with_dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED), err, TAG, "create SPI bus failed");
|
||||||
|
|
||||||
|
if (led_config->flags.invert_out == true) {
|
||||||
|
esp_rom_gpio_connect_out_signal(led_config->strip_gpio_num, spi_periph_signal[spi_strip->spi_host].spid_out, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_device_interface_config_t spi_dev_cfg = {
|
||||||
|
.clock_source = clk_src,
|
||||||
|
.command_bits = 0,
|
||||||
|
.address_bits = 0,
|
||||||
|
.dummy_bits = 0,
|
||||||
|
.clock_speed_hz = LED_STRIP_SPI_DEFAULT_RESOLUTION,
|
||||||
|
.mode = 0,
|
||||||
|
//set -1 when CS is not used
|
||||||
|
.spics_io_num = -1,
|
||||||
|
.queue_size = LED_STRIP_SPI_DEFAULT_TRANS_QUEUE_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_GOTO_ON_ERROR(spi_bus_add_device(spi_strip->spi_host, &spi_dev_cfg, &spi_strip->spi_device), err, TAG, "Failed to add spi device");
|
||||||
|
//ensure the reset time is enough
|
||||||
|
esp_rom_delay_us(10);
|
||||||
|
int clock_resolution_khz = 0;
|
||||||
|
spi_device_get_actual_freq(spi_strip->spi_device, &clock_resolution_khz);
|
||||||
|
// TODO: ideally we should decide the SPI_BYTES_PER_COLOR_BYTE by the real clock resolution
|
||||||
|
// But now, let's fixed the resolution, the downside is, we don't support a clock source whose frequency is not multiple of LED_STRIP_SPI_DEFAULT_RESOLUTION
|
||||||
|
// clock_resolution between 2.2MHz to 2.8MHz is supported
|
||||||
|
ESP_GOTO_ON_FALSE((clock_resolution_khz < LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 + 300) && (clock_resolution_khz > LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000 - 300), ESP_ERR_NOT_SUPPORTED, err,
|
||||||
|
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||||
|
|
||||||
|
if (led_config->led_model != LED_MODEL_WS2812) {
|
||||||
|
ESP_LOGW(TAG, "Only support WS2812. The timing requirements for other models may not be met");
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_strip->component_fmt = component_fmt;
|
||||||
|
spi_strip->bytes_per_pixel = bytes_per_pixel;
|
||||||
|
spi_strip->strip_len = led_config->max_leds;
|
||||||
|
spi_strip->base.set_pixel = led_strip_spi_set_pixel;
|
||||||
|
spi_strip->base.set_pixel_rgbw = led_strip_spi_set_pixel_rgbw;
|
||||||
|
spi_strip->base.refresh = led_strip_spi_refresh;
|
||||||
|
spi_strip->base.clear = led_strip_spi_clear;
|
||||||
|
spi_strip->base.del = led_strip_spi_del;
|
||||||
|
|
||||||
|
*ret_strip = &spi_strip->base;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
if (spi_strip) {
|
||||||
|
if (spi_strip->spi_device) {
|
||||||
|
spi_bus_remove_device(spi_strip->spi_device);
|
||||||
|
}
|
||||||
|
if (spi_strip->spi_host) {
|
||||||
|
spi_bus_free(spi_strip->spi_host);
|
||||||
|
}
|
||||||
|
free(spi_strip);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
17
software/blink/pytest_blink.py
Normal file
17
software/blink/pytest_blink.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded_idf.dut import IdfDut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['supported_targets'], indirect=['target'])
|
||||||
|
def test_blink(dut: IdfDut) -> None:
|
||||||
|
# check and log bin size
|
||||||
|
binary_file = os.path.join(dut.app.binary_path, 'blink.bin')
|
||||||
|
bin_size = os.path.getsize(binary_file)
|
||||||
|
logging.info(f'blink_bin_size : {bin_size // 1024}KB')
|
||||||
2480
software/blink/sdkconfig
Normal file
2480
software/blink/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
2
software/blink/sdkconfig.defaults
Normal file
2
software/blink/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_BLINK_LED_GPIO=y
|
||||||
|
CONFIG_BLINK_GPIO=8
|
||||||
1
software/blink/sdkconfig.defaults.esp32
Normal file
1
software/blink/sdkconfig.defaults.esp32
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_BLINK_GPIO=5
|
||||||
7
software/blink/sdkconfig.defaults.esp32s3
Normal file
7
software/blink/sdkconfig.defaults.esp32s3
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
CONFIG_BLINK_LED_STRIP=y
|
||||||
|
#
|
||||||
|
# Please Note:
|
||||||
|
# ESP32-S3-DevKitC v1.1 uses GPIO38 for the on-board LED
|
||||||
|
# ESP32-S3-DevKitC v1.0 uses GPIO48 for the on-board LED
|
||||||
|
#
|
||||||
|
CONFIG_BLINK_GPIO=38
|
||||||
8
software/hello_world/CMakeLists.txt
Normal file
8
software/hello_world/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.22)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
project(hello_world)
|
||||||
53
software/hello_world/README.md
Normal file
53
software/hello_world/README.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 | ESP32-S31 | Linux |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | ----- |
|
||||||
|
|
||||||
|
# Hello World Example
|
||||||
|
|
||||||
|
Starts a FreeRTOS task to print "Hello World".
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
## How to use example
|
||||||
|
|
||||||
|
Follow detailed instructions provided specifically for this example.
|
||||||
|
|
||||||
|
Select the instructions depending on Espressif chip installed on your development board:
|
||||||
|
|
||||||
|
- [ESP32 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/stable/get-started/index.html)
|
||||||
|
- [ESP32-S2 Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html)
|
||||||
|
|
||||||
|
|
||||||
|
## Example folder contents
|
||||||
|
|
||||||
|
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
|
||||||
|
|
||||||
|
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
|
||||||
|
|
||||||
|
Below is short explanation of remaining files in the project folder.
|
||||||
|
|
||||||
|
```
|
||||||
|
├── CMakeLists.txt
|
||||||
|
├── pytest_hello_world.py Python script used for automated testing
|
||||||
|
├── main
|
||||||
|
│ ├── CMakeLists.txt
|
||||||
|
│ └── hello_world_main.c
|
||||||
|
└── README.md This is the file you are currently reading
|
||||||
|
```
|
||||||
|
|
||||||
|
For more information on structure and contents of ESP-IDF projects, please refer to Section [Build System](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html) of the ESP-IDF Programming Guide.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
* Program upload failure
|
||||||
|
|
||||||
|
* Hardware connection is not correct: run `idf.py -p PORT monitor`, and reboot your board to see if there are any output logs.
|
||||||
|
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
|
||||||
|
|
||||||
|
## Technical support and feedback
|
||||||
|
|
||||||
|
Please use the following feedback channels:
|
||||||
|
|
||||||
|
* For technical queries, go to the [esp32.com](https://esp32.com/) forum
|
||||||
|
* For a feature request or bug report, create a [GitHub issue](https://github.com/espressif/esp-idf/issues)
|
||||||
|
|
||||||
|
We will get back to you as soon as possible.
|
||||||
3
software/hello_world/main/CMakeLists.txt
Normal file
3
software/hello_world/main/CMakeLists.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
idf_component_register(SRCS "hello_world_main.c"
|
||||||
|
PRIV_REQUIRES spi_flash
|
||||||
|
INCLUDE_DIRS "")
|
||||||
52
software/hello_world/main/hello_world_main.c
Normal file
52
software/hello_world/main/hello_world_main.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_chip_info.h"
|
||||||
|
#include "esp_flash.h"
|
||||||
|
#include "esp_system.h"
|
||||||
|
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("Hello world!\n");
|
||||||
|
|
||||||
|
/* Print chip information */
|
||||||
|
esp_chip_info_t chip_info;
|
||||||
|
uint32_t flash_size;
|
||||||
|
esp_chip_info(&chip_info);
|
||||||
|
printf("This is %s chip with %d CPU core(s), %s%s%s%s, ",
|
||||||
|
CONFIG_IDF_TARGET,
|
||||||
|
chip_info.cores,
|
||||||
|
(chip_info.features & CHIP_FEATURE_WIFI_BGN) ? "WiFi/" : "",
|
||||||
|
(chip_info.features & CHIP_FEATURE_BT) ? "BT" : "",
|
||||||
|
(chip_info.features & CHIP_FEATURE_BLE) ? "BLE" : "",
|
||||||
|
(chip_info.features & CHIP_FEATURE_IEEE802154) ? ", 802.15.4 (Zigbee/Thread)" : "");
|
||||||
|
|
||||||
|
unsigned major_rev = chip_info.revision / 100;
|
||||||
|
unsigned minor_rev = chip_info.revision % 100;
|
||||||
|
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
|
||||||
|
if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
|
||||||
|
printf("Get flash size failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%" PRIu32 "MB %s flash\n", flash_size / (uint32_t)(1024 * 1024),
|
||||||
|
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
|
||||||
|
|
||||||
|
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
|
||||||
|
|
||||||
|
for (int i = 10; i >= 0; i--) {
|
||||||
|
printf("Restarting in %d seconds...\n", i);
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
printf("Restarting now.\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
2
software/hello_world/main/idf_component.yml
Normal file
2
software/hello_world/main/idf_component.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^3.0.0"
|
||||||
54
software/hello_world/pytest_hello_world.py
Normal file
54
software/hello_world/pytest_hello_world.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded_idf.dut import IdfDut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
from pytest_embedded_qemu.app import QemuApp
|
||||||
|
from pytest_embedded_qemu.dut import QemuDut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.generic
|
||||||
|
@idf_parametrize('target', ['supported_targets', 'preview_targets'], indirect=['target'])
|
||||||
|
def test_hello_world(dut: IdfDut, log_minimum_free_heap_size: Callable[..., None]) -> None:
|
||||||
|
dut.expect('Hello world!')
|
||||||
|
log_minimum_free_heap_size()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test
|
||||||
|
@idf_parametrize('target', ['linux'], indirect=['target'])
|
||||||
|
def test_hello_world_linux(dut: IdfDut) -> None:
|
||||||
|
dut.expect('Hello world!')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.host_test
|
||||||
|
@pytest.mark.macos
|
||||||
|
@idf_parametrize('target', ['linux'], indirect=['target'])
|
||||||
|
def test_hello_world_macos(dut: IdfDut) -> None:
|
||||||
|
dut.expect('Hello world!')
|
||||||
|
|
||||||
|
|
||||||
|
def verify_elf_sha256_embedding(app: QemuApp, sha256_reported: str) -> None:
|
||||||
|
sha256 = hashlib.sha256()
|
||||||
|
with open(app.elf_file, 'rb') as f:
|
||||||
|
sha256.update(f.read())
|
||||||
|
sha256_expected = sha256.hexdigest()
|
||||||
|
|
||||||
|
logging.info(f'ELF file SHA256: {sha256_expected}')
|
||||||
|
logging.info(f'ELF file SHA256 (reported by the app): {sha256_reported}')
|
||||||
|
|
||||||
|
# the app reports only the first several hex characters of the SHA256, check that they match
|
||||||
|
if not sha256_expected.startswith(sha256_reported):
|
||||||
|
raise ValueError('ELF file SHA256 mismatch')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.qemu
|
||||||
|
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
|
||||||
|
def test_hello_world_host(app: QemuApp, dut: QemuDut) -> None:
|
||||||
|
sha256_reported = dut.expect(r'ELF file SHA256:\s+([a-f0-9]+)').group(1).decode('utf-8')
|
||||||
|
verify_elf_sha256_embedding(app, sha256_reported)
|
||||||
|
|
||||||
|
dut.expect('Hello world!')
|
||||||
2414
software/hello_world/sdkconfig
Normal file
2414
software/hello_world/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
0
software/hello_world/sdkconfig.ci
Normal file
0
software/hello_world/sdkconfig.ci
Normal file
71
software/zig_main/.devcontainer/Dockerfile
Normal file
71
software/zig_main/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
ARG DOCKER_TAG=latest
|
||||||
|
FROM espressif/idf:${DOCKER_TAG}
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=nointeractive
|
||||||
|
ARG CONTAINER_USER=esp
|
||||||
|
ARG USER_UID=1050
|
||||||
|
ARG USER_GID=$USER_UID
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt install -y -q \
|
||||||
|
cmake \
|
||||||
|
git \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libnuma1 \
|
||||||
|
libpixman-1-0 \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# QEMU
|
||||||
|
ENV QEMU_REL=esp_develop_9.2.2_20250817
|
||||||
|
ENV QEMU_SHA256=588bfaccd0f929650655d10a580f020c6ba9c131712d8fa519280081b8d126eb
|
||||||
|
ENV QEMU_DIST=qemu-xtensa-softmmu-${QEMU_REL}-x86_64-linux-gnu.tar.xz
|
||||||
|
ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/esp-develop-9.2.2-20250817/${QEMU_DIST}
|
||||||
|
|
||||||
|
# Zig v0.16.0 - xtensa
|
||||||
|
ENV ZIG_REL=zig-relsafe-x86_64-linux-musl-baseline
|
||||||
|
ENV ZIG_SHA256=411f1858a9610803af28cd271acf4548873545ccc866f4b9060903ff0d4b6e8e
|
||||||
|
ENV ZIG_DIST=${ZIG_REL}.tar.xz
|
||||||
|
ENV ZIG_URL=https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa/${ZIG_DIST}
|
||||||
|
|
||||||
|
# ZLS v0.16.0
|
||||||
|
ENV ZLS_REL=zls-x86_64-linux-musl-baseline
|
||||||
|
ENV ZLS_SHA256=993192ee0e212334df0cc5ae4fc5e5bdfded395054414a4a7b0202e4dd3c74dd
|
||||||
|
ENV ZLS_DIST=${ZLS_REL}.tar.xz
|
||||||
|
ENV ZLS_URL=https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa-dev/${ZLS_DIST}
|
||||||
|
|
||||||
|
ENV LC_ALL=C.UTF-8
|
||||||
|
ENV LANG=C.UTF-8
|
||||||
|
|
||||||
|
RUN wget --no-verbose ${QEMU_URL} \
|
||||||
|
&& echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
|
||||||
|
&& tar -xf $QEMU_DIST -C /opt \
|
||||||
|
&& rm ${QEMU_DIST}
|
||||||
|
|
||||||
|
RUN wget --no-verbose ${ZIG_URL} \
|
||||||
|
&& echo "${ZIG_SHA256} *${ZIG_DIST}" | sha256sum --check --strict - \
|
||||||
|
&& tar -xf $ZIG_DIST -C /opt \
|
||||||
|
&& rm ${ZIG_DIST}
|
||||||
|
|
||||||
|
RUN wget --no-verbose ${ZLS_URL} \
|
||||||
|
&& echo "${ZLS_SHA256} *${ZLS_DIST}" | sha256sum --check --strict - \
|
||||||
|
&& mkdir -p /opt/zls \
|
||||||
|
&& tar -xf $ZLS_DIST -C /opt/zls \
|
||||||
|
&& rm ${ZLS_DIST}
|
||||||
|
|
||||||
|
ENV PATH=/opt/qemu/bin:/opt/${ZIG_REL}:/opt/zls/bin:${PATH}
|
||||||
|
|
||||||
|
RUN groupadd --gid $USER_GID $CONTAINER_USER \
|
||||||
|
&& adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
|
||||||
|
&& usermod -a -G root $CONTAINER_USER && usermod -a -G dialout $CONTAINER_USER
|
||||||
|
|
||||||
|
RUN chmod -R 775 /opt/esp/python_env/
|
||||||
|
|
||||||
|
USER ${CONTAINER_USER}
|
||||||
|
ENV USER=${CONTAINER_USER}
|
||||||
|
WORKDIR /home/${CONTAINER_USER}
|
||||||
|
|
||||||
|
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
|
||||||
|
|
||||||
|
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
|
||||||
|
|
||||||
|
CMD ["/bin/bash", "-c"]
|
||||||
51
software/zig_main/.devcontainer/devcontainer.json
Normal file
51
software/zig_main/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
{
|
||||||
|
"name": "ESP-IDF QEMU",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile",
|
||||||
|
"args": {
|
||||||
|
"DOCKER_TAG": "release-v6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mounts": [
|
||||||
|
// Unfortunately this mount only works for linux users:
|
||||||
|
"source=/dev/bus/usb,target=/dev/bus/usb,type=bind,consistency=cached"
|
||||||
|
],
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.espIdfPath": "/opt/esp/idf",
|
||||||
|
"idf.customExtraPaths": "",
|
||||||
|
"idf.pythonBinPath": "/opt/esp/python_env/idf6.0_py3.14_env/bin/python",
|
||||||
|
"idf.toolsPath": "/opt/esp",
|
||||||
|
"zig.zls.path": "/opt/zls/bin/zls",
|
||||||
|
"zig.path": "/opt/zig-relsafe-x86_64-linux-musl-baseline/zig",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"espressif.esp-idf-extension",
|
||||||
|
"ziglang.vscode-zig"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"codespaces": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.espIdfPath": "/opt/esp/idf",
|
||||||
|
"idf.customExtraPaths": "",
|
||||||
|
"idf.pythonBinPath": "/opt/esp/python_env/idf6.0_py3.14_env/bin/python",
|
||||||
|
"idf.toolsPath": "/opt/esp",
|
||||||
|
"zig.zls.path": "/opt/zls/bin/zls",
|
||||||
|
"zig.path": "/opt/zig-relsafe-x86_64-linux-musl-baseline/zig",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"espressif.esp-idf-extension",
|
||||||
|
"ziglang.vscode-zig",
|
||||||
|
"espressif.esp-idf-web"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runArgs": [
|
||||||
|
"--privileged"
|
||||||
|
]
|
||||||
|
}
|
||||||
13
software/zig_main/.gitignore
vendored
Normal file
13
software/zig_main/.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
build/
|
||||||
|
*zig-*/
|
||||||
|
.vscode/
|
||||||
|
.cache/
|
||||||
|
sdkconfig
|
||||||
|
*.old
|
||||||
|
*.lock
|
||||||
|
managed_components/
|
||||||
|
imports/idf-sys.zig
|
||||||
|
imports/helpers.zig
|
||||||
|
imports/c_builtins.zig
|
||||||
|
include/bindings.h
|
||||||
|
!flake.lock
|
||||||
10
software/zig_main/CMakeLists.txt
Normal file
10
software/zig_main/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose default type of build (Debug)" FORCE)
|
||||||
|
endif()
|
||||||
|
project(zig-sample-idf)
|
||||||
201
software/zig_main/LICENSE-APACHE
Normal file
201
software/zig_main/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
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.
|
||||||
16
software/zig_main/LICENSE-MIT
Normal file
16
software/zig_main/LICENSE-MIT
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
MIT No Attribution
|
||||||
|
|
||||||
|
Copyright 2024 all Contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
without restriction, including without limitation the rights to use, copy, modify,
|
||||||
|
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
150
software/zig_main/README.md
Normal file
150
software/zig_main/README.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
[](https://deepwiki.com/kassane/zig-esp-idf-sample)
|
||||||
|
|
||||||
|
# Using Zig Language & Toolchain with ESP-IDF
|
||||||
|
|
||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-H4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
## STATUS: Experimental
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
This project aims to integrate Zig language and toolchain with the [Espressif IoT Development Framework](https://github.com/espressif/esp-idf) for enhanced development capabilities on ESP32 and its variants.
|
||||||
|
|
||||||
|
More information about building and using Zig with ESP-IDF can be found in the [documentation](docs/getting-started.md).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- [Zig](https://ziglang.org/download) toolchain - v0.16.0 or master
|
||||||
|
- [ESP-IDF](https://github.com/espressif/esp-idf) - v5.x or v6.x or master
|
||||||
|
|
||||||
|
### Targets Allowed
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Target</th>
|
||||||
|
<th>Architecture</th>
|
||||||
|
<th>Features</th>
|
||||||
|
<th>Zig Build Configuration</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32</strong></td>
|
||||||
|
<td>Xtensa LX6</td>
|
||||||
|
<td>Dual-core, WiFi, BT Classic, BLE</td>
|
||||||
|
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-S2</strong></td>
|
||||||
|
<td>Xtensa LX7</td>
|
||||||
|
<td>Single-core, WiFi, USB OTG</td>
|
||||||
|
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32s2</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-S3</strong></td>
|
||||||
|
<td>Xtensa LX7</td>
|
||||||
|
<td>Dual-core, WiFi, BLE 5.0, USB OTG, AI</td>
|
||||||
|
<td><code>-Dtarget=xtensa-freestanding-none -Dcpu=esp32s3</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-C2</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Single-core, WiFi, BLE 5.0, Low-cost</td>
|
||||||
|
<td rowspan="2"><code>-Dtarget=riscv32-freestanding-none -Dcpu=generic_rv32+m+c+zicsr+zifencei</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-C3</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Single-core, WiFi, BLE 5.0, Low-power</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-C5</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Single-core, WiFi 6, BLE 5.0</td>
|
||||||
|
<td rowspan="5"><code>-Dtarget=riscv32-freestanding-none -Dcpu=generic_rv32+m+a+c+zicsr+zifencei</code></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-C6</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Single-core, WiFi 6, BLE 5.0, Zigbee, Thread</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-C61</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Single-core, WiFi 6, BLE 5.0, Low-cost</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-H2</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>BLE 5.0, Zigbee 3.0, Thread, No WiFi</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-H21</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>BLE 5.0, Zigbee 3.0, Thread, No WiFi</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-H4</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>BLE 5.2, Zigbee, Thread, FPU, No WiFi</td>
|
||||||
|
<td><code>-Dtarget=riscv32-freestanding-eabihf -Dcpu=esp32h4</code> (Espressif fork) / <code>generic_rv32+m+a+c+f+zicsr+zifencei</code> (upstream)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><strong>ESP32-P4</strong></td>
|
||||||
|
<td>RISC-V</td>
|
||||||
|
<td>Dual-core, AI, DSP, FPU, No WiFi/BT</td>
|
||||||
|
<td><code>-Dtarget=riscv32-freestanding-eabihf -Dcpu=esp32p4</code> (Espressif fork) / <code>generic_rv32+m+a+c+f+zicsr+zifencei</code> (upstream)</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> **Xtensa Architecture Support**
|
||||||
|
>
|
||||||
|
> The upstream [Zig compiler](https://ziglang.org/download) (LLVM backend) does not support Xtensa architecture. For ESP32, ESP32-S2, and ESP32-S3 targets, you must use the [Espressif Zig fork](https://github.com/kassane/zig-espressif-bootstrap/releases).
|
||||||
|
>
|
||||||
|
> - **RISC-V targets (all variants):** Works with upstream Zig ✅ (uses generic_rv32 fallback); named CPU models available with Espressif fork
|
||||||
|
> - **Xtensa targets (ESP32/S2/S3):** Requires [zig-xtensa](https://github.com/kassane/zig-espressif-bootstrap/releases) (auto-downloaded)
|
||||||
|
>
|
||||||
|
> The build system automatically downloads the correct toolchain for your target.
|
||||||
|
|
||||||
|
|
||||||
|
### Key Features:
|
||||||
|
|
||||||
|
- **Zig Language Integration**: Use the Zig programming language to write firmware code. It provides modern language features such as comptime, meta-programming, and error handling.
|
||||||
|
|
||||||
|
- **Zig Toolchain Integration**: The Zig toolchain can be used to build zig libraries and executables, and can also be integrated with the ESP-IDF build system. Also, system compiler and linker can be replaced to `zig cc`/`zig c++`.
|
||||||
|
- **Note:** For C++ support, zig toolchain uses `llvm-libc++` ABI by default.
|
||||||
|
|
||||||
|
- **ESP-IDF Compatibility**: Seamlessly integrate Zig with the ESP-IDF framework, allowing developers to leverage the rich set of APIs and functionalities provided by ESP-IDF for IoT development.
|
||||||
|
|
||||||
|
- **Build System Configuration**: Using CMake to build Zig libraries allows easy integration with existing ESP-IDF projects while providing efficient dependency management and build configuration.
|
||||||
|
|
||||||
|
- **Cross-Platform Development**: Facilitate development across various ESP32 variants including ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, ESP32-P4, ESP32-S2, and ESP32-S3, ensuring broad compatibility and versatility.
|
||||||
|
|
||||||
|
|
||||||
|
### About Allocators
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
>
|
||||||
|
> Asserts allocations are within `@alignOf(std.c.max_align_t)` and directly calls
|
||||||
|
> `malloc`/`free`. Does not attempt to utilize `malloc_usable_size`.
|
||||||
|
>
|
||||||
|
> - `std.heap.raw_c_allocator` allocator is safe to use as the backing allocator with `std.heap.ArenaAllocator` for example and is more optimal in such a case than `std.heap.c_allocator`. - ref.: [std-doc](https://ziglang.org/documentation/0.15.2/std/#std.heap.raw_c_allocator)
|
||||||
|
>
|
||||||
|
> - `std.heap.ArenaAllocator` takes an existing allocator, wraps it, and provides an interface where you can allocate without freeing, and then free it all together. - ref.: [std-doc](https://ziglang.org/documentation/master/std/#std.heap.ArenaAllocator)
|
||||||
|
>
|
||||||
|
> **Custom Allocators** (based on `std.heap.raw_c_allocator`)
|
||||||
|
>
|
||||||
|
> - `idf.heap.HeapCapsAllocator` - ref.: [espressif-doc](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/mem_alloc.html)
|
||||||
|
> - `idf.heap.MultiHeapAllocator` - ref.: [espressif-doc](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/mem_alloc.html)
|
||||||
|
> - `idf.heap.VPortAllocator` - ref.: [FreeRTOS-doc](https://www.freertos.org/a00111.html)
|
||||||
|
|
||||||
|
|
||||||
|
### License
|
||||||
|
|
||||||
|
This project is licensed twice:
|
||||||
|
- [Apache](LICENSE-APACHE)
|
||||||
|
- [MIT-0](LICENSE-MIT)
|
||||||
258
software/zig_main/build.zig
Normal file
258
software/zig_main/build.zig
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) !void {
|
||||||
|
const target = b.standardTargetOptions(.{
|
||||||
|
.whitelist = espressif_targets,
|
||||||
|
.default_target = espressif_targets[0],
|
||||||
|
});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const example = b.option([]const u8, "example", "Relative path (from project root) to the Zig source file") orelse "main/app.zig";
|
||||||
|
|
||||||
|
const obj = b.addObject(.{
|
||||||
|
.name = "app_zig",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.root_source_file = b.path(example),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.link_libc = true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
obj.root_module.addImport("esp_idf", idf_wrapped_modules(b));
|
||||||
|
|
||||||
|
const obj_install = b.addInstallArtifact(obj, .{
|
||||||
|
.dest_dir = .{
|
||||||
|
.override = .{
|
||||||
|
.custom = "obj",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
b.getInstallStep().dependOn(&obj_install.step);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Module descriptor table
|
||||||
|
//
|
||||||
|
// To add a new module:
|
||||||
|
// 1. Add a row here with the .zig source file and any deps by name.
|
||||||
|
// 2. Add the name to the `esp_idf` entry's deps list if it should be
|
||||||
|
// re-exported through the top-level "esp_idf" namespace module.
|
||||||
|
//
|
||||||
|
// To add a new dependency inside an existing .zig source file:
|
||||||
|
// 1. Add the dep name to the matching row's `deps` field here.
|
||||||
|
// That is the *only* place you need to edit — no other blocks to touch.
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
const ModuleSpec = struct {
|
||||||
|
/// Name used for @import("…") and as the key in the resolver map.
|
||||||
|
name: []const u8,
|
||||||
|
/// Path relative to the `imports/` directory.
|
||||||
|
file: []const u8,
|
||||||
|
/// Names of other modules (must appear earlier in this table).
|
||||||
|
deps: []const []const u8 = &.{},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Ordered list — a module may only reference deps that appear before it.
|
||||||
|
const module_specs = [_]ModuleSpec{
|
||||||
|
// ── leaf (no deps) ──────────────────────────────────────────────────
|
||||||
|
.{ .name = "sys", .file = "idf-sys.zig" },
|
||||||
|
// ── depend on sys only ──────────────────────────────────────────────
|
||||||
|
.{ .name = "error", .file = "error.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "log", .file = "logger.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "ver", .file = "version.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "heap", .file = "heap.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "bootloader", .file = "bootloader.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "lwip", .file = "lwip.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "mqtt", .file = "mqtt.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "phy", .file = "phy.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "segger", .file = "segger.zig", .deps = &.{"sys"} },
|
||||||
|
.{ .name = "crc", .file = "crc.zig", .deps = &.{"sys"} },
|
||||||
|
// ── depend on sys + error ───────────────────────────────────────────
|
||||||
|
.{ .name = "bluetooth", .file = "bluetooth.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "led", .file = "led-strip.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "wifi", .file = "wifi.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "gpio", .file = "gpio.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "uart", .file = "uart.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "i2c", .file = "i2c.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "i2s", .file = "i2s.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "spi", .file = "spi.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "now", .file = "now.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "pulse", .file = "pcnt.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "http", .file = "http.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "dsp", .file = "dsp.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "hosted", .file = "hosted.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "wifi_remote", .file = "wifi_remote.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "pthread", .file = "pthread.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "timer", .file = "timer.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "ledc", .file = "ledc.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "twai", .file = "twai.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "pm", .file = "pm.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "matter", .file = "matter.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "rtos", .file = "rtos.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "nvs", .file = "nvs.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "partition", .file = "partition.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "sleep", .file = "sleep.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "event", .file = "event.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "wdt", .file = "wdt.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
.{ .name = "nimble", .file = "nimble.zig", .deps = &.{ "sys", "error" } },
|
||||||
|
// ── depend on sys + log ────────────────────────
|
||||||
|
.{ .name = "panic", .file = "panic.zig", .deps = &.{ "sys", "log" } },
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Names re-exported by the top-level "esp_idf" umbrella module (idf.zig).
|
||||||
|
const esp_idf_exports = [_][]const u8{
|
||||||
|
"sys", "error", "log", "ver", "heap", "bootloader", "lwip", "mqtt",
|
||||||
|
"phy", "segger", "crc", "bluetooth", "led", "wifi", "gpio", "uart",
|
||||||
|
"i2c", "i2s", "spi", "now", "pulse", "http", "dsp", "panic",
|
||||||
|
"rtos", "nvs", "partition", "sleep", "event", "wdt", "nimble", "hosted",
|
||||||
|
"wifi_remote", "timer", "ledc", "twai", "pm", "pthread", "matter",
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn idf_wrapped_modules(b: *std.Build) *std.Build.Module {
|
||||||
|
const src_path = std.fs.path.dirname(@src().file) orelse b.pathResolve(&.{"."});
|
||||||
|
const imports_dir = b.pathJoin(&.{ src_path, "imports" });
|
||||||
|
|
||||||
|
// Build a name → *Module map so deps can be looked up by name.
|
||||||
|
var map = std.StringHashMap(*std.Build.Module).init(b.allocator);
|
||||||
|
defer map.deinit();
|
||||||
|
|
||||||
|
inline for (module_specs) |spec| {
|
||||||
|
// Collect this module's imports from the already-resolved map.
|
||||||
|
var imports: std.ArrayList(std.Build.Module.Import) = .empty;
|
||||||
|
defer imports.deinit(b.allocator);
|
||||||
|
|
||||||
|
inline for (spec.deps) |dep_name| {
|
||||||
|
const dep_mod = map.get(dep_name) orelse
|
||||||
|
@panic("dep '" ++ dep_name ++ "' not yet resolved — check ordering in module_specs");
|
||||||
|
imports.append(b.allocator, .{ .name = dep_name, .module = dep_mod }) catch @panic("OOM");
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = b.addModule(spec.name, .{
|
||||||
|
.root_source_file = b.path(b.pathJoin(&.{ imports_dir, spec.file })),
|
||||||
|
.imports = imports.items,
|
||||||
|
});
|
||||||
|
map.put(spec.name, mod) catch @panic("OOM");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the esp_idf umbrella module's import list.
|
||||||
|
var top_imports: std.ArrayList(std.Build.Module.Import) = .empty;
|
||||||
|
defer top_imports.deinit(b.allocator);
|
||||||
|
|
||||||
|
inline for (esp_idf_exports) |name| {
|
||||||
|
const mod = map.get(name) orelse
|
||||||
|
@panic("export '" ++ name ++ "' not found in module_specs");
|
||||||
|
top_imports.append(b.allocator, .{ .name = name, .module = mod }) catch @panic("OOM");
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.addModule("esp_idf", .{
|
||||||
|
.root_source_file = b.path(b.pathJoin(&.{ imports_dir, "idf.zig" })),
|
||||||
|
.imports = top_imports.items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const espressif_targets: []const std.Target.Query =
|
||||||
|
if (hasEspXtensaSupport()) riscv_targets ++ xtensa_targets else riscv_targets;
|
||||||
|
|
||||||
|
const riscv_targets: []const std.Target.Query = blk: {
|
||||||
|
var result: []const std.Target.Query = &.{};
|
||||||
|
|
||||||
|
// Named ESP32 RISC-V CPU models — available when built with the Espressif Zig fork.
|
||||||
|
// MC group (c2, c3): m+c+zicsr+zifencei abi=none
|
||||||
|
// MAC group (c5, c6, c61, h2, h21): m+a+c+zicsr+zifencei abi=none
|
||||||
|
// MACF group (h4, s31, p4, p4eco4): m+a+c+f+zicsr+zifencei abi=eabihf
|
||||||
|
const plain_models = .{
|
||||||
|
"esp32c2", "esp32c3",
|
||||||
|
"esp32c5", "esp32c6",
|
||||||
|
"esp32c61", "esp32c61eco0",
|
||||||
|
"esp32h2", "esp32h21",
|
||||||
|
};
|
||||||
|
for (plain_models) |name| {
|
||||||
|
if (@hasDecl(std.Target.riscv.cpu, name)) {
|
||||||
|
result = result ++ &[_]std.Target.Query{.{
|
||||||
|
.cpu_arch = .riscv32,
|
||||||
|
.cpu_model = .{ .explicit = &@field(std.Target.riscv.cpu, name) },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MACF group: has FPU — use eabihf ABI.
|
||||||
|
const macf_models = .{
|
||||||
|
"esp32h4", "esp32p4",
|
||||||
|
"esp32p4eco4", "esp32s31",
|
||||||
|
};
|
||||||
|
for (macf_models) |name| {
|
||||||
|
if (@hasDecl(std.Target.riscv.cpu, name)) {
|
||||||
|
result = result ++ &[_]std.Target.Query{.{
|
||||||
|
.cpu_arch = .riscv32,
|
||||||
|
.cpu_model = .{ .explicit = &@field(std.Target.riscv.cpu, name) },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .eabihf,
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic fallback targets for standard Zig builds without named ESP models.
|
||||||
|
if (result.len == 0) {
|
||||||
|
result = &[_]std.Target.Query{
|
||||||
|
// MC: c2, c3
|
||||||
|
.{
|
||||||
|
.cpu_arch = .riscv32,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .c, .zifencei, .zicsr }),
|
||||||
|
},
|
||||||
|
// MAC: c5, c6, c61, h2, h21
|
||||||
|
.{
|
||||||
|
.cpu_arch = .riscv32,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .a, .c, .zifencei, .zicsr }),
|
||||||
|
},
|
||||||
|
// MACF: h4, s31, p4, p4eco4
|
||||||
|
.{
|
||||||
|
.cpu_arch = .riscv32,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.riscv.cpu.generic_rv32 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .eabihf,
|
||||||
|
.cpu_features_add = std.Target.riscv.featureSet(&.{ .m, .a, .c, .f, .zifencei, .zicsr }),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const xtensa_targets: []const std.Target.Query = &.{
|
||||||
|
// ESP32: Requires Espressif LLVM fork
|
||||||
|
.{
|
||||||
|
.cpu_arch = .xtensa,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
},
|
||||||
|
// ESP32-S2: Requires Espressif LLVM fork
|
||||||
|
.{
|
||||||
|
.cpu_arch = .xtensa,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32s2 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
},
|
||||||
|
// ESP32-S3: Requires Espressif LLVM fork
|
||||||
|
.{
|
||||||
|
.cpu_arch = .xtensa,
|
||||||
|
.cpu_model = .{ .explicit = &std.Target.xtensa.cpu.esp32s3 },
|
||||||
|
.os_tag = .freestanding,
|
||||||
|
.abi = .none,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Checks if the Zig compiler has Espressif Xtensa support enabled
|
||||||
|
fn hasEspXtensaSupport() bool {
|
||||||
|
for (std.Target.Cpu.Arch.xtensa.allCpuModels()) |model| {
|
||||||
|
if (std.mem.startsWith(u8, model.name, "esp")) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
54
software/zig_main/build.zig.zon
Normal file
54
software/zig_main/build.zig.zon
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
.{
|
||||||
|
.name = .esp_idf_sample,
|
||||||
|
.version = "0.1.0",
|
||||||
|
.fingerprint = 0xbe5a38cf3bc380ae,
|
||||||
|
// This field is optional.
|
||||||
|
// Each dependency must either provide a `url` and `hash`, or a `path`.
|
||||||
|
// `zig build --fetch` can be used to fetch all dependencies of a package, recursively.
|
||||||
|
// Once all dependencies are fetched, `zig build` no longer requires
|
||||||
|
// internet connectivity.
|
||||||
|
.dependencies = .{
|
||||||
|
// See `zig fetch --save <url>` for a command-line interface for adding dependencies.
|
||||||
|
//.example = .{
|
||||||
|
// // When updating this field to a new URL, be sure to delete the corresponding
|
||||||
|
// // `hash`, otherwise you are communicating that you expect to find the old hash at
|
||||||
|
// // the new URL.
|
||||||
|
// .url = "https://example.com/foo.tar.gz",
|
||||||
|
//
|
||||||
|
// // This is computed from the file contents of the directory of files that is
|
||||||
|
// // obtained after fetching `url` and applying the inclusion rules given by
|
||||||
|
// // `paths`.
|
||||||
|
// //
|
||||||
|
// // This field is the source of truth; packages do not come from a `url`; they
|
||||||
|
// // come from a `hash`. `url` is just one of many possible mirrors for how to
|
||||||
|
// // obtain a package matching this `hash`.
|
||||||
|
// //
|
||||||
|
// // Uses the [multihash](https://multiformats.io/multihash/) format.
|
||||||
|
// .hash = "...",
|
||||||
|
//
|
||||||
|
// // When this is provided, the package is found in a directory relative to the
|
||||||
|
// // build root. In this case the package's hash is irrelevant and therefore not
|
||||||
|
// // computed. This field and `url` are mutually exclusive.
|
||||||
|
// .path = "foo",
|
||||||
|
//},
|
||||||
|
},
|
||||||
|
// Specifies the set of files and directories that are included in this package.
|
||||||
|
// Only files and directories listed here are included in the `hash` that
|
||||||
|
// is computed for this package.
|
||||||
|
// Paths are relative to the build root. Use the empty string (`""`) to refer to
|
||||||
|
// the build root itself.
|
||||||
|
// A directory listed here means that all files within, recursively, are included.
|
||||||
|
.paths = .{
|
||||||
|
// This makes *all* files, recursively, included in this package. It is generally
|
||||||
|
// better to explicitly list the files and directories instead, to insure that
|
||||||
|
// fetching from tarballs, file system paths, and version control all result
|
||||||
|
// in the same contents hash.
|
||||||
|
"main",
|
||||||
|
"imports",
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"LICENSE-APACHE",
|
||||||
|
"LICENSE-MIT",
|
||||||
|
"README.md",
|
||||||
|
},
|
||||||
|
}
|
||||||
158
software/zig_main/cmake/bindgen-standalone.cmake
Normal file
158
software/zig_main/cmake/bindgen-standalone.cmake
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Download translate_c-standalone
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
# Determine platform triple for translate_c-standalone naming
|
||||||
|
if(ZIG_PLATFORM STREQUAL "linux-musl")
|
||||||
|
if(ZIG_ARCH STREQUAL "aarch64")
|
||||||
|
set(TRANSLATE_C_TRIPLE "aarch64-linux-musl")
|
||||||
|
set(HASH_SUM "7d5968a41a02064d90035db95ef102fc2ab5e71a2eb48ba6cf028eb79b4ae43d")
|
||||||
|
elseif(ZIG_ARCH STREQUAL "x86_64")
|
||||||
|
set(TRANSLATE_C_TRIPLE "x86_64-linux-musl")
|
||||||
|
set(HASH_SUM "d098e2675fc6706914b6e53dd72f6536895420460f287e73e19772c979fabb84")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
set(ARCHIVE_EXT "tar.xz")
|
||||||
|
elseif(ZIG_PLATFORM STREQUAL "windows")
|
||||||
|
if(ZIG_ARCH STREQUAL "x86_64")
|
||||||
|
set(TRANSLATE_C_TRIPLE "x86_64-windows-gnu")
|
||||||
|
set(HASH_SUM "8e53a03ee44983eea2786c2ba028e83a816b9a993a768b3f4ca8c6e3525923e2")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
set(ARCHIVE_EXT "zip")
|
||||||
|
elseif(ZIG_PLATFORM STREQUAL "macos")
|
||||||
|
if(ZIG_ARCH STREQUAL "aarch64")
|
||||||
|
set(TRANSLATE_C_TRIPLE "aarch64-macos")
|
||||||
|
set(HASH_SUM "8b3d2360d09add35f764ac138e0ec497503dc3de788a01e93fd7b9923a44369c")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture for translate_c-standalone: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
set(ARCHIVE_EXT "tar.xz")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported platform for translate_c-standalone: ${ZIG_PLATFORM}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(TRANSLATE_C_DIR "${CMAKE_BINARY_DIR}/translate_c-standalone")
|
||||||
|
set(TRANSLATE_C_ARCHIVE "${CMAKE_BINARY_DIR}/translate_c-standalone-${TRANSLATE_C_TRIPLE}-baseline.${ARCHIVE_EXT}")
|
||||||
|
set(TRANSLATE_C_BIN "${TRANSLATE_C_DIR}/bin")
|
||||||
|
|
||||||
|
# Download if not already extracted
|
||||||
|
if(NOT EXISTS "${TRANSLATE_C_BIN}")
|
||||||
|
set(TRANSLATE_C_URL "https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa-dev/translate_c-standalone-${TRANSLATE_C_TRIPLE}-baseline.${ARCHIVE_EXT}")
|
||||||
|
|
||||||
|
message(STATUS "Downloading translate_c-standalone (${TRANSLATE_C_TRIPLE}):")
|
||||||
|
message(STATUS " => ${TRANSLATE_C_ARCHIVE}")
|
||||||
|
|
||||||
|
file(DOWNLOAD "${TRANSLATE_C_URL}" "${TRANSLATE_C_ARCHIVE}"
|
||||||
|
TLS_VERIFY ON
|
||||||
|
EXPECTED_HASH SHA256=${HASH_SUM}
|
||||||
|
STATUS download_status
|
||||||
|
LOG download_log
|
||||||
|
)
|
||||||
|
|
||||||
|
list(GET download_status 0 dl_code)
|
||||||
|
if(NOT dl_code EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Download failed:\n${download_log}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Extracting ${ARCHIVE_EXT} ...")
|
||||||
|
file(MAKE_DIRECTORY "${TRANSLATE_C_DIR}")
|
||||||
|
|
||||||
|
if(HOST_OS_LOWER MATCHES "windows|win")
|
||||||
|
execute_process(
|
||||||
|
COMMAND powershell -NoProfile -ExecutionPolicy Bypass
|
||||||
|
-Command "Expand-Archive -Path '${TRANSLATE_C_ARCHIVE}' -DestinationPath '${TRANSLATE_C_DIR}' -Force"
|
||||||
|
RESULT_VARIABLE extract_result
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E tar xf "${TRANSLATE_C_ARCHIVE}"
|
||||||
|
WORKING_DIRECTORY "${TRANSLATE_C_DIR}"
|
||||||
|
RESULT_VARIABLE extract_result
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT extract_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Extraction failed (code ${extract_result})")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(REMOVE "${TRANSLATE_C_ARCHIVE}")
|
||||||
|
|
||||||
|
# Make executable on Unix
|
||||||
|
if(NOT HOST_OS_LOWER MATCHES "windows|win")
|
||||||
|
execute_process(
|
||||||
|
COMMAND chmod +x "${TRANSLATE_C_BIN}"
|
||||||
|
RESULT_VARIABLE chmod_result
|
||||||
|
)
|
||||||
|
if(NOT chmod_result EQUAL 0)
|
||||||
|
message(WARNING "Failed to set executable bit on translate_c-standalone")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "translate_c-standalone ready at: ${TRANSLATE_C_BIN}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Using cached translate_c-standalone: ${TRANSLATE_C_BIN}")
|
||||||
|
endif()
|
||||||
|
set(BINDGEN "${TRANSLATE_C_BIN}/translate-c")
|
||||||
|
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Helper to invoke translate-c (mirrors zig_run from zig-runner.cmake)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
function(bindgen_run)
|
||||||
|
cmake_parse_arguments(PARSE_ARGV 0 ARG
|
||||||
|
"VERBATIM;ALLOW_FAIL"
|
||||||
|
"WORKING_DIRECTORY;RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE;OUTPUT_FILE;TIMEOUT"
|
||||||
|
"COMMAND"
|
||||||
|
)
|
||||||
|
if(NOT ARG_COMMAND)
|
||||||
|
message(FATAL_ERROR "bindgen_run: COMMAND list is required")
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED ARG_WORKING_DIRECTORY)
|
||||||
|
set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED ARG_TIMEOUT)
|
||||||
|
set(ARG_TIMEOUT 300)
|
||||||
|
endif()
|
||||||
|
set(extra_args)
|
||||||
|
if(ARG_VERBATIM)
|
||||||
|
list(APPEND extra_args VERBATIM)
|
||||||
|
endif()
|
||||||
|
if(ARG_OUTPUT_FILE)
|
||||||
|
list(APPEND extra_args OUTPUT_FILE "${ARG_OUTPUT_FILE}")
|
||||||
|
endif()
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${BINDGEN}" ${ARG_COMMAND}
|
||||||
|
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
OUTPUT_VARIABLE output
|
||||||
|
ERROR_VARIABLE error
|
||||||
|
TIMEOUT ${ARG_TIMEOUT}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_STRIP_TRAILING_WHITESPACE
|
||||||
|
${extra_args}
|
||||||
|
)
|
||||||
|
if(DEFINED ARG_RESULT_VARIABLE)
|
||||||
|
set(${ARG_RESULT_VARIABLE} "${result}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ARG_OUTPUT_VARIABLE)
|
||||||
|
set(${ARG_OUTPUT_VARIABLE} "${output}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ARG_ERROR_VARIABLE)
|
||||||
|
set(${ARG_ERROR_VARIABLE} "${error}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(NOT ARG_ALLOW_FAIL)
|
||||||
|
if(result MATCHES "timeout")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"translate-c timed out after ${ARG_TIMEOUT}s — Aro deadlock on stub headers (issue #61)\n"
|
||||||
|
" Command: ${BINDGEN} ${ARG_COMMAND}\n"
|
||||||
|
"--- stderr ---\n${error}")
|
||||||
|
elseif(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"translate-c failed (code ${result}):\n"
|
||||||
|
" ${BINDGEN} ${ARG_COMMAND}\n"
|
||||||
|
"--- stderr ---\n${error}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
32
software/zig_main/cmake/bindings.cmake
Normal file
32
software/zig_main/cmake/bindings.cmake
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# ────────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Helper for downloading / referencing esp-rs/esp-idf-sys bindings.h
|
||||||
|
#
|
||||||
|
# This file is part of a Zig + ESP-IDF project using translate-c.
|
||||||
|
# It downloads the official bindings.h mega-header from esp-rs/esp-idf-sys
|
||||||
|
# as a reference (helps when improving stubs.h or comparing generated bindings).
|
||||||
|
#
|
||||||
|
# Huge thanks to the esp-rs community for maintaining esp-idf-sys
|
||||||
|
# Repository: https://github.com/esp-rs/esp-idf-sys
|
||||||
|
# bindings.h: https://github.com/esp-rs/esp-idf-sys/blob/master/src/include/esp-idf/bindings.h
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
set(BINDINGS_URL
|
||||||
|
"https://raw.githubusercontent.com/esp-rs/esp-idf-sys/refs/heads/master/src/include/esp-idf/bindings.h"
|
||||||
|
)
|
||||||
|
set(BINDINGS_ESP_RS "${CMAKE_SOURCE_DIR}/include/bindings.h")
|
||||||
|
if(NOT EXISTS "${BINDINGS_ESP_RS}")
|
||||||
|
file(DOWNLOAD ${BINDINGS_URL} ${BINDINGS_ESP_RS}
|
||||||
|
TLS_VERIFY ON
|
||||||
|
STATUS download_status
|
||||||
|
LOG download_log
|
||||||
|
TIMEOUT 15
|
||||||
|
)
|
||||||
|
list(GET download_status 0 status_code)
|
||||||
|
list(GET download_status 1 error_msg)
|
||||||
|
if(NOT status_code EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to download esp-idf/bindings.h\nStatus: ${status_code}\nError: ${error_msg}\nLog:\n${download_log}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Downloaded esp-idf/bindings.h => ${BINDINGS_ESP_RS}")
|
||||||
|
else()
|
||||||
|
message(STATUS "esp-idf/bindings.h already exists => ${BINDINGS_ESP_RS}")
|
||||||
|
endif()
|
||||||
227
software/zig_main/cmake/extra-components.cmake
Normal file
227
software/zig_main/cmake/extra-components.cmake
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
# ─── Optional/Managed Components Detection ───────────────────────────────────
|
||||||
|
idf_build_get_property(BUILD_COMPS BUILD_COMPONENTS)
|
||||||
|
|
||||||
|
# Get IDF_PATH from environment
|
||||||
|
set(IDF_PATH $ENV{IDF_PATH})
|
||||||
|
|
||||||
|
# Include version.cmake to get IDF_VERSION_MAJOR, IDF_VERSION_MINOR, etc.
|
||||||
|
if(EXISTS "${IDF_PATH}/tools/cmake/version.cmake")
|
||||||
|
include("${IDF_PATH}/tools/cmake/version.cmake")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Failed to find version.cmake in ${IDF_PATH}/tools/cmake")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Helper function to check and add component
|
||||||
|
macro(check_unmanaged_component COMPONENT_NAME VENDOR PACKAGE DEFINE_NAME)
|
||||||
|
set(COMP_PATHS "")
|
||||||
|
set(COMP_BASE "${CMAKE_SOURCE_DIR}/components/${VENDOR}__${PACKAGE}")
|
||||||
|
|
||||||
|
if("${PACKAGE}" STREQUAL "esp-dsp")
|
||||||
|
list(APPEND COMP_PATHS
|
||||||
|
"${COMP_BASE}/modules/common/include"
|
||||||
|
"${COMP_BASE}/modules/dotprod/include"
|
||||||
|
"${COMP_BASE}/modules/support/include"
|
||||||
|
"${COMP_BASE}/modules/support/mem/include"
|
||||||
|
"${COMP_BASE}/modules/windows/include"
|
||||||
|
"${COMP_BASE}/modules/windows/hann/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman_harris/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman_nuttall/include"
|
||||||
|
"${COMP_BASE}/modules/windows/nuttall/include"
|
||||||
|
"${COMP_BASE}/modules/windows/flat_top/include"
|
||||||
|
"${COMP_BASE}/modules/iir/include"
|
||||||
|
"${COMP_BASE}/modules/fir/include"
|
||||||
|
"${COMP_BASE}/modules/math/include"
|
||||||
|
"${COMP_BASE}/modules/math/add/include"
|
||||||
|
"${COMP_BASE}/modules/math/sub/include"
|
||||||
|
"${COMP_BASE}/modules/math/mul/include"
|
||||||
|
"${COMP_BASE}/modules/math/addc/include"
|
||||||
|
"${COMP_BASE}/modules/math/mulc/include"
|
||||||
|
"${COMP_BASE}/modules/math/sqrt/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/mul/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/add/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/addc/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/mulc/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/sub/include"
|
||||||
|
"${COMP_BASE}/modules/fft/include"
|
||||||
|
"${COMP_BASE}/modules/dct/include"
|
||||||
|
"${COMP_BASE}/modules/conv/include"
|
||||||
|
"${COMP_BASE}/modules/kalman/ekf/include"
|
||||||
|
"${COMP_BASE}/modules/kalman/ekf_imu13states/include"
|
||||||
|
)
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_wifi_remote")
|
||||||
|
# Add base include if exists
|
||||||
|
set(BASE_INCLUDE "${COMP_BASE}/include")
|
||||||
|
if(EXISTS "${BASE_INCLUDE}")
|
||||||
|
list(APPEND COMP_PATHS "${BASE_INCLUDE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add version-specific include using MAJOR.MINOR
|
||||||
|
set(VERSION_SUBDIR "idf_v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||||
|
set(VERSION_PATH "${COMP_BASE}/${VERSION_SUBDIR}/include")
|
||||||
|
if(EXISTS "${VERSION_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${VERSION_PATH}")
|
||||||
|
endif()
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_hosted")
|
||||||
|
# Add host and api include paths for esp_hosted
|
||||||
|
set(HOST_PATH "${COMP_BASE}/host")
|
||||||
|
if(EXISTS "${HOST_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${HOST_PATH}")
|
||||||
|
endif()
|
||||||
|
set(API_PATH "${HOST_PATH}/api/include")
|
||||||
|
if(EXISTS "${API_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${API_PATH}")
|
||||||
|
endif()
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_matter")
|
||||||
|
# esp_matter is a C++ (CHIP SDK) library.
|
||||||
|
# matter_stubs.h provides the C interface for translate-c instead.
|
||||||
|
list(APPEND COMP_PATHS "${COMP_BASE}/components/esp_matter")
|
||||||
|
else()
|
||||||
|
list(APPEND COMP_PATHS "${COMP_BASE}/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check if component is in build
|
||||||
|
if("${VENDOR}__${PACKAGE}" IN_LIST BUILD_COMPS OR "${PACKAGE}" IN_LIST BUILD_COMPS)
|
||||||
|
# Verify at least one path exists (check the base directory)
|
||||||
|
if(EXISTS "${COMP_BASE}")
|
||||||
|
message(STATUS "${COMPONENT_NAME} component found")
|
||||||
|
set(${DEFINE_NAME} 1)
|
||||||
|
|
||||||
|
# Add all include paths that actually exist
|
||||||
|
set(VALID_PATHS 0)
|
||||||
|
foreach(COMP_PATH IN LISTS COMP_PATHS)
|
||||||
|
if(EXISTS "${COMP_PATH}")
|
||||||
|
list(APPEND INCLUDE_DIRS "${COMP_PATH}")
|
||||||
|
math(EXPR VALID_PATHS "${VALID_PATHS} + 1")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(VALID_PATHS GREATER 0)
|
||||||
|
message(STATUS " Added ${VALID_PATHS} include paths for ${COMPONENT_NAME}")
|
||||||
|
else()
|
||||||
|
message(WARNING "${COMPONENT_NAME} base exists but no include paths found")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "${COMPONENT_NAME} in components but not found: ${COMP_BASE}")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "${COMPONENT_NAME} not in build. To add: idf.py add-dependency ${VENDOR}/${PACKAGE}")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND EXTRA_DEFINE_FLAGS "-D${DEFINE_NAME}=${${DEFINE_NAME}}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Helper function to check and add component
|
||||||
|
macro(check_managed_component COMPONENT_NAME VENDOR PACKAGE DEFINE_NAME)
|
||||||
|
set(COMP_PATHS "")
|
||||||
|
set(COMP_BASE "${CMAKE_SOURCE_DIR}/managed_components/${VENDOR}__${PACKAGE}")
|
||||||
|
|
||||||
|
if("${PACKAGE}" STREQUAL "esp-dsp")
|
||||||
|
list(APPEND COMP_PATHS
|
||||||
|
"${COMP_BASE}/modules/common/include"
|
||||||
|
"${COMP_BASE}/modules/dotprod/include"
|
||||||
|
"${COMP_BASE}/modules/support/include"
|
||||||
|
"${COMP_BASE}/modules/support/mem/include"
|
||||||
|
"${COMP_BASE}/modules/windows/include"
|
||||||
|
"${COMP_BASE}/modules/windows/hann/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman_harris/include"
|
||||||
|
"${COMP_BASE}/modules/windows/blackman_nuttall/include"
|
||||||
|
"${COMP_BASE}/modules/windows/nuttall/include"
|
||||||
|
"${COMP_BASE}/modules/windows/flat_top/include"
|
||||||
|
"${COMP_BASE}/modules/iir/include"
|
||||||
|
"${COMP_BASE}/modules/fir/include"
|
||||||
|
"${COMP_BASE}/modules/math/include"
|
||||||
|
"${COMP_BASE}/modules/math/add/include"
|
||||||
|
"${COMP_BASE}/modules/math/sub/include"
|
||||||
|
"${COMP_BASE}/modules/math/mul/include"
|
||||||
|
"${COMP_BASE}/modules/math/addc/include"
|
||||||
|
"${COMP_BASE}/modules/math/mulc/include"
|
||||||
|
"${COMP_BASE}/modules/math/sqrt/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/mul/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/add/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/addc/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/mulc/include"
|
||||||
|
"${COMP_BASE}/modules/matrix/sub/include"
|
||||||
|
"${COMP_BASE}/modules/fft/include"
|
||||||
|
"${COMP_BASE}/modules/dct/include"
|
||||||
|
"${COMP_BASE}/modules/conv/include"
|
||||||
|
"${COMP_BASE}/modules/kalman/ekf/include"
|
||||||
|
"${COMP_BASE}/modules/kalman/ekf_imu13states/include"
|
||||||
|
)
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_wifi_remote")
|
||||||
|
# Add base include if exists
|
||||||
|
set(BASE_INCLUDE "${COMP_BASE}/include")
|
||||||
|
if(EXISTS "${BASE_INCLUDE}")
|
||||||
|
list(APPEND COMP_PATHS "${BASE_INCLUDE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add version-specific include using MAJOR.MINOR
|
||||||
|
set(VERSION_SUBDIR "idf_v${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}")
|
||||||
|
set(VERSION_PATH "${COMP_BASE}/${VERSION_SUBDIR}/include")
|
||||||
|
if(EXISTS "${VERSION_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${VERSION_PATH}")
|
||||||
|
endif()
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_hosted")
|
||||||
|
# Add host and api include paths for esp_hosted
|
||||||
|
set(HOST_PATH "${COMP_BASE}/host")
|
||||||
|
if(EXISTS "${HOST_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${HOST_PATH}")
|
||||||
|
endif()
|
||||||
|
set(API_PATH "${HOST_PATH}/api/include")
|
||||||
|
if(EXISTS "${API_PATH}")
|
||||||
|
list(APPEND COMP_PATHS "${API_PATH}")
|
||||||
|
endif()
|
||||||
|
elseif("${PACKAGE}" STREQUAL "esp_matter")
|
||||||
|
# esp_matter is a C++ (CHIP SDK) library.
|
||||||
|
# matter_stubs.h provides the C interface for translate-c instead.
|
||||||
|
list(APPEND COMP_PATHS "${COMP_BASE}/components/esp_matter")
|
||||||
|
else()
|
||||||
|
list(APPEND COMP_PATHS "${COMP_BASE}/include")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Check if component is in build
|
||||||
|
if("${VENDOR}__${PACKAGE}" IN_LIST BUILD_COMPS OR "${PACKAGE}" IN_LIST BUILD_COMPS)
|
||||||
|
# Verify at least one path exists (check the base directory)
|
||||||
|
if(EXISTS "${COMP_BASE}")
|
||||||
|
message(STATUS "${COMPONENT_NAME} component found")
|
||||||
|
set(${DEFINE_NAME} 1)
|
||||||
|
|
||||||
|
# Add all include paths that actually exist
|
||||||
|
set(VALID_PATHS 0)
|
||||||
|
foreach(COMP_PATH IN LISTS COMP_PATHS)
|
||||||
|
if(EXISTS "${COMP_PATH}")
|
||||||
|
list(APPEND INCLUDE_DIRS "${COMP_PATH}")
|
||||||
|
math(EXPR VALID_PATHS "${VALID_PATHS} + 1")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(VALID_PATHS GREATER 0)
|
||||||
|
message(STATUS " Added ${VALID_PATHS} include paths for ${COMPONENT_NAME}")
|
||||||
|
else()
|
||||||
|
message(WARNING "${COMPONENT_NAME} base exists but no include paths found")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "${COMPONENT_NAME} in components but not found: ${COMP_BASE}")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "${COMPONENT_NAME} not in build. To add: idf.py add-dependency ${VENDOR}/${PACKAGE}")
|
||||||
|
set(${DEFINE_NAME} 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND EXTRA_DEFINE_FLAGS "-D${DEFINE_NAME}=${${DEFINE_NAME}}")
|
||||||
|
endmacro()
|
||||||
|
|
||||||
|
# Add your components here
|
||||||
|
check_unmanaged_component("LED Strip" "espressif" "led_strip" "HAS_LED_STRIP")
|
||||||
|
check_managed_component("ESP-DSP" "espressif" "esp-dsp" "HAS_ESP_DSP")
|
||||||
|
check_managed_component("ESP Wifi Remote" "espressif" "esp_wifi_remote" "HAS_ESP_WIFI_REMOTE")
|
||||||
|
check_managed_component("ESP Hosted" "espressif" "esp_hosted" "HAS_ESP_HOSTED")
|
||||||
|
check_managed_component("ESP Matter" "espressif" "esp_matter" "HAS_ESP_MATTER")
|
||||||
162
software/zig_main/cmake/patch.cmake
Normal file
162
software/zig_main/cmake/patch.cmake
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# Zig Bindings Patcher
|
||||||
|
# Removes and replaces problematic structs/functions from ESP-IDF bindings
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
message(STATUS "Patching Zig bindings: ${TARGET_FILE}")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Read file content
|
||||||
|
# ============================================================================
|
||||||
|
file(READ "${TARGET_FILE}" FILE_CONTENT)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Determine WiFi support based on target
|
||||||
|
# ============================================================================
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32P4 OR CONFIG_IDF_TARGET_ESP32H2 OR CONFIG_IDF_TARGET_ESP32H21 OR CONFIG_IDF_TARGET_ESP32H4)
|
||||||
|
set(WIFI_SUPPORTED FALSE)
|
||||||
|
else()
|
||||||
|
set(WIFI_SUPPORTED TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Component status (passed from zig-config.cmake)
|
||||||
|
# ============================================================================
|
||||||
|
if(NOT DEFINED HAS_LED_STRIP)
|
||||||
|
set(HAS_LED_STRIP 0)
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED HAS_ESP_DSP)
|
||||||
|
set(HAS_ESP_DSP 0)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# message(STATUS "Component detection:")
|
||||||
|
# message(STATUS " HAS_LED_STRIP: ${HAS_LED_STRIP}")
|
||||||
|
# message(STATUS " HAS_ESP_DSP: ${HAS_ESP_DSP}")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Remove problematic definitions
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# WiFi patches (only for WiFi-enabled targets)
|
||||||
|
if(WIFI_SUPPORTED)
|
||||||
|
string(REGEX REPLACE "pub const wifi_sta_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const wifi_ap_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Remove portTICK_PERIOD_MS (will be replaced with custom version)
|
||||||
|
string(REGEX REPLACE "pub const portTICK_PERIOD_MS[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Handle bitfield-related opaque types and unions
|
||||||
|
# ============================================================================
|
||||||
|
string(REGEX REPLACE "(//[^\n]*\n?)*const struct_unnamed_[0-9]+ = opaque {};( //[^\n]*\n?)*" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "(//[^\n]*\n?)*const union_unnamed_[0-9]+ = extern union \\{\n unnamed_0: struct_unnamed_[0-9]+,\n val: u32,\n\\};( //[^\n]*\n?)*" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "unnamed_0: struct_unnamed_[0-9]+,\n?" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "([a-zA-Z0-9_]+): ((\[[0-9]+\])?)(struct|union)_unnamed_[0-9]+" "\\1: \\2u32" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "zeroes\\((struct|union)_unnamed_[0-9]+\\)" "zeroes(u32)" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "zeroes\\((\\[[0-9]+\\])(struct|union)_unnamed_[0-9]+\\)" "zeroes(\\1u32)" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Fix peripheral register structs (lp_*, gpio_dev_t, i2c_dev_t, etc.)
|
||||||
|
# ============================================================================
|
||||||
|
# 1. Aggressively remove ALL gpio_dev_t and lp_* declarations (including opaque and aliases)
|
||||||
|
string(REGEX REPLACE "pub const (struct_)?gpio_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const (struct_)?lp_[a-z0-9_]*_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const (struct_)?i2c_dev_t = [^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
# 2. Also remove any remaining aliases that point to struct_ versions
|
||||||
|
string(REGEX REPLACE "pub const (lp_[a-z0-9_]+_dev_t|gpio_dev_t|i2c_dev_t) = struct_[^;]+;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
# 3. Append clean definitions with correct sizes
|
||||||
|
string(APPEND FILE_CONTENT "
|
||||||
|
pub const lp_io_dev_t = extern struct { reserved: [1024]u8 };
|
||||||
|
pub const lp_clkrst_dev_t = extern struct { reserved: [1024]u8 };
|
||||||
|
pub const lp_i2c_dev_t = extern struct { reserved: [0x184]u8 };
|
||||||
|
pub const lp_timer_dev_t = extern struct { reserved: [1024]u8 };
|
||||||
|
pub const lp_uart_dev_t = extern struct { reserved: [0xa0]u8 };
|
||||||
|
pub const lp_wdt_dev_t = extern struct { reserved: [1024]u8 };
|
||||||
|
pub const lp_io_mux_dev_t = extern struct { reserved: [512]u8 };
|
||||||
|
pub const lp_iomux_dev_t = extern struct { reserved: [84]u8 };
|
||||||
|
pub const lp_aonclkrst_dev_t = extern struct { reserved: [1024]u8 };
|
||||||
|
pub const i2c_dev_t = extern struct { reserved: [388]u8 };
|
||||||
|
")
|
||||||
|
# 4. Add gpio_dev_t with target-specific size (this must come AFTER the general remove)
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32C61 OR CONFIG_IDF_TARGET_ESP32C5 OR CONFIG_IDF_TARGET_ESP32H4)
|
||||||
|
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [3584]u8 };")
|
||||||
|
elseif(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
|
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [2048]u8 };")
|
||||||
|
elseif(CONFIG_IDF_TARGET_ESP32C2 OR CONFIG_IDF_TARGET_ESP32C6 OR CONFIG_IDF_TARGET_ESP32H2)
|
||||||
|
string(APPEND FILE_CONTENT "\npub const gpio_dev_t = extern struct { reserved: [1792]u8 };")
|
||||||
|
else()
|
||||||
|
# Fallback
|
||||||
|
endif()
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
|
string(APPEND FILE_CONTENT "\npub const lp_gpio_dev_t = extern struct { reserved: [308]u8 };")
|
||||||
|
else()
|
||||||
|
string(APPEND FILE_CONTENT "\npub const lp_gpio_dev_t = extern struct { reserved: [1024]u8 };")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ESP32-P4 specific: Remove xPortCanYield function
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
|
string(REGEX REPLACE "pub fn xPortCanYield\\([^)]*\\) callconv\\(\\.c\\) bool \\{([^{}]|\\{[^{}]*\\})*\\}" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# LED Strip component patches (if enabled)
|
||||||
|
if(HAS_LED_STRIP EQUAL 1)
|
||||||
|
message(STATUS " Applying LED Strip patches")
|
||||||
|
string(REGEX REPLACE "pub const struct_led_strip_rmt_extra_config_20[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const struct_format_layout_15[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const led_strip_rmt_config_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const led_color_component_format_t[^;]*;" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
string(REGEX REPLACE "pub const led_strip_config_t = extern struct \\{[^}]*\\};" "" FILE_CONTENT "${FILE_CONTENT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Append custom patch files
|
||||||
|
# ============================================================================
|
||||||
|
set(PATCH_DIR "${CMAKE_SOURCE_DIR}/../../../patches")
|
||||||
|
|
||||||
|
# Define patches to apply
|
||||||
|
set(PATCH_FILES
|
||||||
|
"porttick_period_ms.zig"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add target-specific patches
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
|
list(APPEND PATCH_FILES "xport_can_yield.zig")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add WiFi patches
|
||||||
|
if(WIFI_SUPPORTED)
|
||||||
|
list(APPEND PATCH_FILES
|
||||||
|
"wifi/wifi_sta_config_t.zig"
|
||||||
|
"wifi/wifi_ap_config_t.zig"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add LED Strip patches
|
||||||
|
if(HAS_LED_STRIP EQUAL 1)
|
||||||
|
list(APPEND PATCH_FILES
|
||||||
|
"led_strip/led_strip_rmt_extra_config_20.zig"
|
||||||
|
"led_strip/led_strip_struct_format_layout_15.zig"
|
||||||
|
"led_strip/led_color_component_format_t.zig"
|
||||||
|
"led_strip/led_strip_rmt_config_t.zig"
|
||||||
|
"led_strip/led_strip_config_t.zig"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Apply each patch file
|
||||||
|
foreach(PATCH_FILE IN LISTS PATCH_FILES)
|
||||||
|
set(PATCH_PATH "${PATCH_DIR}/${PATCH_FILE}")
|
||||||
|
if(EXISTS "${PATCH_PATH}")
|
||||||
|
message(STATUS " Applying patch: ${PATCH_FILE}")
|
||||||
|
file(READ "${PATCH_PATH}" PATCH_CONTENT)
|
||||||
|
string(APPEND FILE_CONTENT "\n${PATCH_CONTENT}")
|
||||||
|
else()
|
||||||
|
message(WARNING " Patch file not found: ${PATCH_FILE}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# Write patched content
|
||||||
|
# ============================================================================
|
||||||
|
file(WRITE "${TARGET_FILE}" "${FILE_CONTENT}")
|
||||||
|
message(STATUS "Patching completed: ${TARGET_FILE}")
|
||||||
488
software/zig_main/cmake/zig-config.cmake
Normal file
488
software/zig_main/cmake/zig-config.cmake
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# Zig configuration for ESP-IDF (esp32 xtensa/riscv targets)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
set(ZIG_MIN_VERSION "0.16.0")
|
||||||
|
|
||||||
|
# ─── Host platform & architecture detection ──────────────────────────────────
|
||||||
|
cmake_host_system_information(RESULT HOST_OS QUERY OS_NAME)
|
||||||
|
string(TOLOWER "${HOST_OS}" HOST_OS_LOWER)
|
||||||
|
|
||||||
|
set(HOST_ARCH "${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||||
|
|
||||||
|
# Normalize to Zig-style triple names
|
||||||
|
if(HOST_ARCH MATCHES "^(AMD64|amd64|x86_64|X64)$")
|
||||||
|
set(ZIG_ARCH "x86_64")
|
||||||
|
elseif(HOST_ARCH MATCHES "^(aarch64|arm64|ARM64)$")
|
||||||
|
set(ZIG_ARCH "aarch64")
|
||||||
|
elseif(HOST_ARCH MATCHES "^(x86|i686|i386|i[345]86)$")
|
||||||
|
set(ZIG_ARCH "x86")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported host architecture '${HOST_ARCH}'.\n"
|
||||||
|
"Please report:\n"
|
||||||
|
" CMAKE_HOST_SYSTEM_PROCESSOR = ${CMAKE_HOST_SYSTEM_PROCESSOR}\n"
|
||||||
|
" cmake --system-information | findstr PROCESSOR")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Platform + extension
|
||||||
|
if(HOST_OS_LOWER MATCHES "(linux|unix)")
|
||||||
|
set(ZIG_PLATFORM "linux-musl")
|
||||||
|
set(ARCHIVE_EXT "tar.xz")
|
||||||
|
elseif(HOST_OS_LOWER MATCHES "windows|win")
|
||||||
|
set(ZIG_PLATFORM "windows")
|
||||||
|
set(ARCHIVE_EXT "zip")
|
||||||
|
elseif(HOST_OS_LOWER MATCHES "darwin|mac|osx")
|
||||||
|
set(ZIG_PLATFORM "macos")
|
||||||
|
set(ARCHIVE_EXT "tar.xz")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported host OS: ${HOST_OS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ZIG_TRIPLET "${ZIG_ARCH}-${ZIG_PLATFORM}-baseline")
|
||||||
|
set(ZIG_DIR "${CMAKE_BINARY_DIR}/zig-relsafe-${ZIG_TRIPLET}")
|
||||||
|
set(ZIG_ARCHIVE "${ZIG_DIR}.${ARCHIVE_EXT}")
|
||||||
|
|
||||||
|
# ─── Decide which zig to use ─────────────────────────────────────────────────
|
||||||
|
find_program(ZIG_FOUND zig)
|
||||||
|
|
||||||
|
set(USE_ZIG_ESPRESSIF_BOOTSTRAP TRUE)
|
||||||
|
if(ZIG_FOUND AND CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||||
|
# For most RISC-V prefer system zig (except P4 & H4)
|
||||||
|
if(NOT (CONFIG_IDF_TARGET_ESP32P4 OR CONFIG_IDF_TARGET_ESP32H4))
|
||||||
|
set(USE_ZIG_ESPRESSIF_BOOTSTRAP FALSE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ─── Download & extract espressif zig if needed ──────────────────────────────
|
||||||
|
if(USE_ZIG_ESPRESSIF_BOOTSTRAP)
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/zig-download.cmake)
|
||||||
|
else()
|
||||||
|
if(NOT ZIG_FOUND)
|
||||||
|
message(FATAL_ERROR "System 'zig' not found and espressif bootstrap is disabled.")
|
||||||
|
endif()
|
||||||
|
set(ZIG_BIN "${ZIG_FOUND}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Using Zig executable: ${ZIG_BIN}")
|
||||||
|
|
||||||
|
# ────────────────────────────────────────────────────────────────────────────────────
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/zig-runner.cmake)
|
||||||
|
# ─────── get Zig version ────────────────────────────────────────────────────────────
|
||||||
|
zig_run(COMMAND version OUTPUT_VARIABLE ZIG_VERSION)
|
||||||
|
if("${ZIG_VERSION}" VERSION_LESS "${ZIG_MIN_VERSION}")
|
||||||
|
message(FATAL_ERROR "Zig version too old: ${ZIG_VERSION} < ${ZIG_MIN_VERSION}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Zig version: ${ZIG_VERSION}")
|
||||||
|
|
||||||
|
# Detect whether this Zig build is the Espressif fork (has named ESP32 RISC-V CPU models).
|
||||||
|
# Upstream Zig only ships generic RISC-V models; the fork adds esp32c3, esp32c6, etc.
|
||||||
|
zig_run(
|
||||||
|
COMMAND build-obj --show-builtin -target riscv32-freestanding -mcpu=esp32c3
|
||||||
|
RESULT_VARIABLE _zig_esp_riscv_probe
|
||||||
|
OUTPUT_VARIABLE _zig_esp_riscv_probe_out
|
||||||
|
ERROR_VARIABLE _zig_esp_riscv_probe_err
|
||||||
|
ALLOW_FAIL
|
||||||
|
)
|
||||||
|
if(_zig_esp_riscv_probe EQUAL 0)
|
||||||
|
set(ZIG_HAS_ESP_RISCV_MODELS ON)
|
||||||
|
message(STATUS "Zig RISC-V: Espressif fork detected — using named ESP CPU models")
|
||||||
|
else()
|
||||||
|
set(ZIG_HAS_ESP_RISCV_MODELS OFF)
|
||||||
|
message(STATUS "Zig RISC-V: upstream Zig detected — falling back to generic_rv32 models")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# ====================================================================================
|
||||||
|
|
||||||
|
# Determine target model from CONFIG_IDF_TARGET
|
||||||
|
string(TOLOWER "${CONFIG_IDF_TARGET}" TARGET_IDF_MODEL)
|
||||||
|
|
||||||
|
# Target architecture configuration lookup table
|
||||||
|
set(RISCV_TARGETS
|
||||||
|
"esp32c2" "esp32c3" "esp32c5" "esp32c6" "esp32c61" "esp32c61eco0"
|
||||||
|
"esp32h2" "esp32h21" "esp32h4" "esp32p4" "esp32p4eco4" "esp32s31")
|
||||||
|
set(XTENSA_TARGETS
|
||||||
|
"esp32" "esp32s2" "esp32s3")
|
||||||
|
|
||||||
|
if(TARGET_IDF_MODEL IN_LIST RISCV_TARGETS)
|
||||||
|
set(TARGET_IDF_ARCH "riscv")
|
||||||
|
|
||||||
|
# MACF group (FPU): use eabihf ABI. All others use none.
|
||||||
|
set(_rv_macf_targets "esp32h4" "esp32s31" "esp32p4" "esp32p4eco4")
|
||||||
|
if(TARGET_IDF_MODEL IN_LIST _rv_macf_targets)
|
||||||
|
set(ZIG_TARGET "riscv32-freestanding-eabihf")
|
||||||
|
else()
|
||||||
|
set(ZIG_TARGET "riscv32-freestanding-none")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ZIG_HAS_ESP_RISCV_MODELS)
|
||||||
|
# Espressif Zig fork: use named CPU model (feature set is already encoded in model).
|
||||||
|
set(TARGET_CPU_MODEL "${TARGET_IDF_MODEL}")
|
||||||
|
else()
|
||||||
|
# Upstream Zig: fall back to generic_rv32 with explicit feature sets.
|
||||||
|
# MC (c2, c3): m+c+zicsr+zifencei abi=none
|
||||||
|
# MAC (c5, c6, c61, h2, h21): m+a+c+zicsr+zifencei abi=none
|
||||||
|
# MACF (h4, s31, p4, p4eco4): m+a+c+f+zicsr+zifencei abi=eabihf
|
||||||
|
set(_rv_mc_targets "esp32c2" "esp32c3")
|
||||||
|
set(_rv_mac_targets "esp32c5" "esp32c6" "esp32c61" "esp32c61eco0" "esp32h2" "esp32h21")
|
||||||
|
if(TARGET_IDF_MODEL IN_LIST _rv_mc_targets)
|
||||||
|
set(TARGET_CPU_MODEL "generic_rv32+m+c+zicsr+zifencei")
|
||||||
|
elseif(TARGET_IDF_MODEL IN_LIST _rv_mac_targets)
|
||||||
|
set(TARGET_CPU_MODEL "generic_rv32+m+a+c+zicsr+zifencei")
|
||||||
|
elseif(TARGET_IDF_MODEL IN_LIST _rv_macf_targets)
|
||||||
|
set(TARGET_CPU_MODEL "generic_rv32+m+a+c+f+zicsr+zifencei")
|
||||||
|
else()
|
||||||
|
set(TARGET_CPU_MODEL "generic_rv32+m+c+zicsr+zifencei")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
elseif(TARGET_IDF_MODEL IN_LIST XTENSA_TARGETS)
|
||||||
|
set(TARGET_IDF_ARCH "xtensa")
|
||||||
|
set(ZIG_TARGET "xtensa-freestanding-none")
|
||||||
|
set(TARGET_CPU_MODEL "${TARGET_IDF_MODEL}")
|
||||||
|
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported IDF target: ${CONFIG_IDF_TARGET}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "ESP-IDF Target: ${TARGET_IDF_MODEL}")
|
||||||
|
message(STATUS "Architecture: ${TARGET_IDF_ARCH}")
|
||||||
|
message(STATUS "Zig Target: ${ZIG_TARGET}")
|
||||||
|
message(STATUS "CPU Model: ${TARGET_CPU_MODEL}")
|
||||||
|
|
||||||
|
# Check Toolchain version
|
||||||
|
get_filename_component(TOOLCHAIN_BIN_DIR "${CMAKE_C_COMPILER}" DIRECTORY)
|
||||||
|
get_filename_component(TOOLCHAIN_VERSION_DIR "${TOOLCHAIN_BIN_DIR}" DIRECTORY)
|
||||||
|
if("${TOOLCHAIN_VERSION_DIR}" MATCHES "esp-([0-9]+\\.[0-9]+\\.[0-9]+_[0-9]+)")
|
||||||
|
set(TOOLCHAIN_VERSION "${CMAKE_MATCH_1}")
|
||||||
|
message(STATUS "Detected ESP toolchain version: ${TOOLCHAIN_VERSION}")
|
||||||
|
else()
|
||||||
|
message(WARNING "Standard ESP version pattern not found in: ${TOOLCHAIN_VERSION_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||||
|
set(ARCH "riscv")
|
||||||
|
set(TRIPLE "riscv32-esp-elf")
|
||||||
|
set(ARCH_DEFINE "__riscv")
|
||||||
|
elseif(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||||
|
set(ARCH "xtensa")
|
||||||
|
set(TRIPLE "xtensa-esp-elf")
|
||||||
|
set(ARCH_DEFINE "__XTENSA__")
|
||||||
|
endif()
|
||||||
|
# Get toolchain includes with fallback paths
|
||||||
|
set(POSSIBLE_INCLUDE_PATHS
|
||||||
|
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/include"
|
||||||
|
"${TOOLCHAIN_BIN_DIR}/../${TRIPLE}/include"
|
||||||
|
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/include"
|
||||||
|
)
|
||||||
|
# Find the toolchain include directory
|
||||||
|
set(TOOLCHAIN_ELF_INCLUDE "")
|
||||||
|
foreach(PATH ${POSSIBLE_INCLUDE_PATHS})
|
||||||
|
if(IS_DIRECTORY "${PATH}")
|
||||||
|
set(TOOLCHAIN_ELF_INCLUDE "${PATH}")
|
||||||
|
message(STATUS "Found toolchain include at: ${TOOLCHAIN_ELF_INCLUDE}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
# sys-include should be sys-include directory OR the same as regular include
|
||||||
|
# (since sys headers are typically under include/sys/)
|
||||||
|
set(POSSIBLE_SYS_INCLUDE_PATHS
|
||||||
|
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/sys-include"
|
||||||
|
"${TOOLCHAIN_BIN_DIR}/../${TRIPLE}/sys-include"
|
||||||
|
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/sys-include"
|
||||||
|
"${TOOLCHAIN_VERSION_DIR}/${TRIPLE}/${TRIPLE}/include"
|
||||||
|
"${TOOLCHAIN_ELF_INCLUDE}"
|
||||||
|
)
|
||||||
|
# Find the first existing sys-include directory
|
||||||
|
set(TOOLCHAIN_SYS_INCLUDE "")
|
||||||
|
foreach(PATH ${POSSIBLE_SYS_INCLUDE_PATHS})
|
||||||
|
if(IS_DIRECTORY "${PATH}")
|
||||||
|
set(TOOLCHAIN_SYS_INCLUDE "${PATH}")
|
||||||
|
message(STATUS "Found sys-include at: ${TOOLCHAIN_SYS_INCLUDE}")
|
||||||
|
break()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
if(NOT IS_DIRECTORY "${TOOLCHAIN_ELF_INCLUDE}")
|
||||||
|
message(WARNING "Toolchain include directory not found: ${TOOLCHAIN_ELF_INCLUDE}")
|
||||||
|
endif()
|
||||||
|
if(NOT IS_DIRECTORY "${TOOLCHAIN_SYS_INCLUDE}")
|
||||||
|
message(WARNING "Toolchain sys-include directory not found: ${TOOLCHAIN_SYS_INCLUDE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# components list
|
||||||
|
set(INCLUDE_DIRS
|
||||||
|
# Wrapper dir MUST come first — redirects "freertos/portmacro.h" through a
|
||||||
|
# single canonical path to fix Windows mixed-separator #pragma once failure.
|
||||||
|
# See include/freertos-compat/freertos/portmacro.h for details (issue #50).
|
||||||
|
"${CMAKE_SOURCE_DIR}/include/freertos-compat"
|
||||||
|
"${IDF_PATH}/components/freertos/FreeRTOS-Kernel-SMP/portable/${ARCH}/include/freertos"
|
||||||
|
"${IDF_PATH}/components/freertos/FreeRTOS-Kernel/include"
|
||||||
|
"${IDF_PATH}/components/freertos/config/include"
|
||||||
|
"${IDF_PATH}/components/freertos/config/include/freertos"
|
||||||
|
"${IDF_PATH}/components/freertos/esp_additions/include"
|
||||||
|
"${IDF_PATH}/components/freertos/config/${ARCH}/include"
|
||||||
|
"${IDF_PATH}/components/esp_hw_support/include"
|
||||||
|
"${IDF_PATH}/components/soc/include"
|
||||||
|
"${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/include"
|
||||||
|
"${IDF_PATH}/components/esp_common/include"
|
||||||
|
"${IDF_PATH}/components/hal/include"
|
||||||
|
"${IDF_PATH}/components/bootloader_support/include"
|
||||||
|
"${IDF_PATH}/components/${ARCH}/include"
|
||||||
|
"${IDF_PATH}/components/bt/include/${TARGET_IDF_MODEL}/include"
|
||||||
|
"${IDF_PATH}/components/bt/host/nimble/nimble/nimble/include"
|
||||||
|
"${IDF_PATH}/components/bt/host/bluedroid/api/include"
|
||||||
|
"${IDF_PATH}/components/bt/host/bluedroid/api/include/api"
|
||||||
|
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include"
|
||||||
|
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}"
|
||||||
|
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}/config"
|
||||||
|
"${IDF_PATH}/components/${ARCH}/${TARGET_IDF_MODEL}/include/${ARCH}/core"
|
||||||
|
"${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register"
|
||||||
|
"${IDF_PATH}/components/esp_system/include"
|
||||||
|
"${IDF_PATH}/components/esp_hw_support/etm/include"
|
||||||
|
"${IDF_PATH}/components/esp_hw_support/ldo/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_ana_cmpr/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_ana_conv/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_cam/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_dma/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_gpio/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_gpio/${TARGET_IDF_MODEL}/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_gpspi/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_i2c/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_i2s/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_lcd/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_ledc/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_mcpwm/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_mspi/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_parlio/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_pcnt/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_ppa/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_rmt/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_timg/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_touch_sens/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_twai/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_uart/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_usb/include"
|
||||||
|
"${IDF_PATH}/components/esp_hal_wdt/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_ana_cmpr/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_bitscrambler/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_cam/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_dac/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_gpio/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_gptimer/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_i2c/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_i2s/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_i3c/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_isp/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_jpeg/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_ledc/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_mcpwm/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_parlio/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_pcnt/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_ppa/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_rmt/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_sd_intf/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_sdio/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_sdm/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_sdmmc/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_sdspi/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_spi/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_touch_sens/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_tsens/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_twai/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_uart/include"
|
||||||
|
"${IDF_PATH}/components/esp_driver_usb_serial_jtag/include"
|
||||||
|
"${IDF_PATH}/components/esp_phy/include"
|
||||||
|
"${IDF_PATH}/components/esp_tee/include"
|
||||||
|
"${IDF_PATH}/components/esp_timer/include"
|
||||||
|
"${IDF_PATH}/components/esp_coex/include"
|
||||||
|
"${IDF_PATH}/components/esp_psram/include"
|
||||||
|
"${IDF_PATH}/components/esp_security/include"
|
||||||
|
"${IDF_PATH}/components/esp_trace/include"
|
||||||
|
"${IDF_PATH}/components/esp_blockdev/include"
|
||||||
|
"${IDF_PATH}/components/pthread/include"
|
||||||
|
"${IDF_PATH}/components/hal/platform_port/include"
|
||||||
|
"${IDF_PATH}/components/heap/include"
|
||||||
|
"${IDF_PATH}/components/ieee802154/include"
|
||||||
|
"${IDF_PATH}/components/openthread/include"
|
||||||
|
"${IDF_PATH}/components/openthread/openthread/third_party/mbedtls/repo/include"
|
||||||
|
"${IDF_PATH}/components/esp_rom/${TARGET_IDF_MODEL}/include/${TARGET_IDF_MODEL}"
|
||||||
|
"${IDF_PATH}/components/esp_rom/include"
|
||||||
|
"${IDF_PATH}/components/esp_wifi/include"
|
||||||
|
"${IDF_PATH}/components/esp_bootloader_format/include"
|
||||||
|
"${IDF_PATH}/components/esp_app_format/include"
|
||||||
|
"${IDF_PATH}/components/esp_pm/include"
|
||||||
|
"${IDF_PATH}/components/esp_lcd/include"
|
||||||
|
"${IDF_PATH}/components/esp_lcd/interface"
|
||||||
|
"${IDF_PATH}/components/esp_lcd/dsi/include"
|
||||||
|
"${IDF_PATH}/components/esp_lcd/rgb/include"
|
||||||
|
"${IDF_PATH}/components/mbedtls/mbedtls/tf-psa-crypto/drivers/builtin/include"
|
||||||
|
"${IDF_PATH}/components/mbedtls/mbedtls/tf-psa-crypto/include"
|
||||||
|
"${IDF_PATH}/components/mbedtls/esp_crt_bundle/include"
|
||||||
|
"${IDF_PATH}/components/mbedtls/port/include"
|
||||||
|
"${IDF_PATH}/components/mbedtls/mbedtls/include"
|
||||||
|
"${IDF_PATH}/components/http_parser"
|
||||||
|
"${IDF_PATH}/components/esp-tls"
|
||||||
|
"${IDF_PATH}/components/esp_https_ota/include"
|
||||||
|
"${IDF_PATH}/components/esp_http_server/include"
|
||||||
|
"${IDF_PATH}/components/esp_https_server/include"
|
||||||
|
"${IDF_PATH}/components/esp_http_client/include"
|
||||||
|
"${IDF_PATH}/components/log/include"
|
||||||
|
"${IDF_PATH}/components/vfs/include"
|
||||||
|
"${IDF_PATH}/components/wpa_supplicant/esp_supplicant/include"
|
||||||
|
"${IDF_PATH}/components/nvs_flash/include"
|
||||||
|
"${IDF_PATH}/components/esp_partition/include"
|
||||||
|
"${IDF_PATH}/components/esp_netif/include"
|
||||||
|
"${IDF_PATH}/components/esp_event/include"
|
||||||
|
"${IDF_PATH}/components/driver/i2c/include"
|
||||||
|
"${IDF_PATH}/components/driver/deprecated"
|
||||||
|
"${IDF_PATH}/components/driver/touch_sensor/${TARGET_IDF_MODEL}/include"
|
||||||
|
"${IDF_PATH}/components/driver/touch_sensor/include"
|
||||||
|
"${IDF_PATH}/components/driver/twai/include"
|
||||||
|
"${IDF_PATH}/components/spi_flash/include"
|
||||||
|
"${IDF_PATH}/components/esp_vfs_console/include"
|
||||||
|
"${IDF_PATH}/components/esp_ringbuf/include"
|
||||||
|
"${IDF_PATH}/components/esp_usb_cdc_rom_console/include"
|
||||||
|
"${CMAKE_SOURCE_DIR}/build/config"
|
||||||
|
)
|
||||||
|
# Toolchain system includes (separate from regular includes)
|
||||||
|
set(SYSTEM_INCLUDE_DIRS
|
||||||
|
"${TOOLCHAIN_SYS_INCLUDE}"
|
||||||
|
"${TOOLCHAIN_ELF_INCLUDE}"
|
||||||
|
"${IDF_PATH}/components/newlib"
|
||||||
|
"${IDF_PATH}/components/newlib/platform_include"
|
||||||
|
"${IDF_PATH}/components/esp_libc/platform_include"
|
||||||
|
"${IDF_PATH}/components/lwip/lwip/src/include"
|
||||||
|
"${IDF_PATH}/components/lwip/port/esp32xx/include"
|
||||||
|
"${IDF_PATH}/components/lwip/port/freertos/include"
|
||||||
|
"${IDF_PATH}/components/lwip/port/include"
|
||||||
|
"${IDF_PATH}/components/lwip/include"
|
||||||
|
"${IDF_PATH}/components/lwip/include/apps"
|
||||||
|
)
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32P4)
|
||||||
|
list(APPEND INCLUDE_DIRS "${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register/hw_ver3")
|
||||||
|
elseif(CONFIG_IDF_TARGET_ESP32H4)
|
||||||
|
list(APPEND INCLUDE_DIRS "${IDF_PATH}/components/soc/${TARGET_IDF_MODEL}/register/hw_ver_mp")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# get esp-idf C Macros
|
||||||
|
idf_build_get_property(all_defines COMPILE_DEFINITIONS)
|
||||||
|
|
||||||
|
set(EXTRA_DEFINE_FLAGS "")
|
||||||
|
foreach(def ${all_defines})
|
||||||
|
string(STRIP "${def}" def_clean)
|
||||||
|
if(NOT def_clean STREQUAL "")
|
||||||
|
if(NOT def_clean MATCHES "^-D")
|
||||||
|
list(APPEND EXTRA_DEFINE_FLAGS "-D${def_clean}")
|
||||||
|
else()
|
||||||
|
list(APPEND EXTRA_DEFINE_FLAGS "${def_clean}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
string(TOUPPER "${TARGET_IDF_ARCH}" TARGET_IDF_ARCH_UPPER)
|
||||||
|
string(TOUPPER "${TARGET_IDF_MODEL}" TARGET_IDF_MODEL_UPPER)
|
||||||
|
set(DEFINE_FLAGS
|
||||||
|
"-D__${TARGET_IDF_ARCH}"
|
||||||
|
"-Dcpu_${TARGET_CPU_MODEL}"
|
||||||
|
"-D${ARCH_DEFINE}"
|
||||||
|
"-D__${TARGET_IDF_ARCH_UPPER}_EL__"
|
||||||
|
"-DCONFIG_IDF_TARGET_${TARGET_IDF_MODEL_UPPER}"
|
||||||
|
"-D__COUNTER__=0"
|
||||||
|
"-DIRAM_ATTR="
|
||||||
|
"-D_SECTION_ATTR_IMPL\\(x,y\\)="
|
||||||
|
"-DSOC_MMU_PAGE_SIZE=0x8000"
|
||||||
|
"-DLWIP_NO_UNISTD_H="
|
||||||
|
)
|
||||||
|
string(JOIN " " DEFINE_FLAGS_STR ${DEFINE_FLAGS})
|
||||||
|
|
||||||
|
if(ARCH_DEFINE)
|
||||||
|
set(DEFINE_FLAGS "${DEFINE_FLAGS} -D${ARCH_DEFINE}")
|
||||||
|
endif()
|
||||||
|
set(IDF_SYS_ZIG "${CMAKE_SOURCE_DIR}/imports/idf-sys.zig")
|
||||||
|
set(IDF_SYS_C "${CMAKE_SOURCE_DIR}/include/stubs.h")
|
||||||
|
|
||||||
|
# get esp-rs bindings.h
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/bindings.cmake)
|
||||||
|
|
||||||
|
# add extra-components
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/extra-components.cmake)
|
||||||
|
|
||||||
|
set(INCLUDE_FLAGS "")
|
||||||
|
foreach(dir ${INCLUDE_DIRS})
|
||||||
|
# Normalize to forward slashes (prevents Windows mixed-separator double-include in translate-c)
|
||||||
|
file(TO_CMAKE_PATH "${dir}" dir)
|
||||||
|
set(INCLUDE_FLAGS "${INCLUDE_FLAGS} -I\"${dir}\"")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Build system include flags (for toolchain)
|
||||||
|
foreach(dir ${SYSTEM_INCLUDE_DIRS})
|
||||||
|
file(TO_CMAKE_PATH "${dir}" dir)
|
||||||
|
set(INCLUDE_FLAGS "${INCLUDE_FLAGS} -isystem \"${dir}\"")
|
||||||
|
endforeach()
|
||||||
|
if(NOT WIN32)
|
||||||
|
separate_arguments(INCLUDE_FLAGS UNIX_COMMAND "${INCLUDE_FLAGS}")
|
||||||
|
else()
|
||||||
|
separate_arguments(INCLUDE_FLAGS WINDOWS_COMMAND "${INCLUDE_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Run `translate-c` to generate `idf-sys.zig`
|
||||||
|
include(${CMAKE_SOURCE_DIR}/cmake/bindgen-standalone.cmake)
|
||||||
|
bindgen_run(
|
||||||
|
COMMAND
|
||||||
|
${IDF_SYS_C} -target ${ZIG_TARGET} -mcpu ${TARGET_CPU_MODEL}
|
||||||
|
${DEFINE_FLAGS} ${EXTRA_DEFINE_FLAGS} ${INCLUDE_FLAGS}
|
||||||
|
OUTPUT_FILE ${IDF_SYS_ZIG}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/imports
|
||||||
|
DEPENDS ${IDF_SYS_C}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(PATCHES_DONE "${CMAKE_BINARY_DIR}/patches_applied.done")
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${PATCHES_DONE}"
|
||||||
|
COMMAND ${CMAKE_COMMAND}
|
||||||
|
-D TARGET_FILE=${IDF_SYS_ZIG}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32H2=${CONFIG_IDF_TARGET_ESP32H2}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32H21=${CONFIG_IDF_TARGET_ESP32H21}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32H4=${CONFIG_IDF_TARGET_ESP32H4}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32P4=${CONFIG_IDF_TARGET_ESP32P4}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32C2=${CONFIG_IDF_TARGET_ESP32C2}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32C5=${CONFIG_IDF_TARGET_ESP32C5}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32C6=${CONFIG_IDF_TARGET_ESP32C6}
|
||||||
|
-D CONFIG_IDF_TARGET_ESP32C61=${CONFIG_IDF_TARGET_ESP32C61}
|
||||||
|
-D HAS_LED_STRIP=${HAS_LED_STRIP}
|
||||||
|
-D HAS_ESP_DSP=${HAS_ESP_DSP}
|
||||||
|
-P ${CMAKE_SOURCE_DIR}/cmake/patch.cmake
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E touch "${PATCHES_DONE}"
|
||||||
|
DEPENDS "${IDF_SYS_ZIG}"
|
||||||
|
COMMENT "Patching idf-sys.zig"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
add_custom_target(translate_c ALL DEPENDS "${PATCHES_DONE}")
|
||||||
|
message(STATUS "IDF_SYS_ZIG is set to: ${IDF_SYS_ZIG}")
|
||||||
|
|
||||||
|
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(ZIG_BUILD_TYPE "Debug")
|
||||||
|
else()
|
||||||
|
set(ZIG_BUILD_TYPE "ReleaseSafe")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_target(zig_build
|
||||||
|
${ZIG_BIN} build
|
||||||
|
--build-file ${CMAKE_SOURCE_DIR}/build.zig
|
||||||
|
-Doptimize=${ZIG_BUILD_TYPE}
|
||||||
|
-Dtarget=${ZIG_TARGET}
|
||||||
|
-Dcpu=${TARGET_CPU_MODEL}
|
||||||
|
${ZIG_EXAMPLE_ARG}
|
||||||
|
-freference-trace
|
||||||
|
--cache-dir ${CMAKE_BINARY_DIR}/../.zig-cache
|
||||||
|
--prefix ${CMAKE_BINARY_DIR}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
BYPRODUCTS ${CMAKE_BINARY_DIR}/obj/app_zig.o
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_dependencies(zig_build translate_c)
|
||||||
|
add_dependencies(${COMPONENT_LIB} zig_build)
|
||||||
|
|
||||||
|
target_sources(${COMPONENT_LIB}
|
||||||
|
PRIVATE
|
||||||
|
${CMAKE_BINARY_DIR}/obj/app_zig.o
|
||||||
|
)
|
||||||
58
software/zig_main/cmake/zig-download.cmake
Normal file
58
software/zig_main/cmake/zig-download.cmake
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
if(NOT EXISTS "${ZIG_DIR}/zig")
|
||||||
|
if(ZIG_PLATFORM STREQUAL "linux-musl")
|
||||||
|
if(ZIG_ARCH STREQUAL "aarch64")
|
||||||
|
set(HASH_SUM "d3e4930bbac053b40860290cdec2ad1e052418172aa452e590b242c081a01f94")
|
||||||
|
elseif(ZIG_ARCH STREQUAL "x86_64")
|
||||||
|
set(HASH_SUM "411f1858a9610803af28cd271acf4548873545ccc866f4b9060903ff0d4b6e8e")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
elseif(ZIG_PLATFORM STREQUAL "windows")
|
||||||
|
if(ZIG_ARCH STREQUAL "x86_64")
|
||||||
|
set(HASH_SUM "4d5b66d857e790d068e408e6bdd0054f9e78ca2d0ab96b5ce25f63a125f1701e")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
elseif(ZIG_PLATFORM STREQUAL "macos")
|
||||||
|
if(ZIG_ARCH STREQUAL "aarch64")
|
||||||
|
set(HASH_SUM "f47ac7927ae44f3b8290aa818d3158b0f9adc12742e03b7496ad440b6d89d38e")
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture: ${ZIG_ARCH}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
set(ZIG_URL "https://github.com/kassane/zig-espressif-bootstrap/releases/download/0.16.0-xtensa/zig-relsafe-${ZIG_TRIPLET}.${ARCHIVE_EXT}")
|
||||||
|
message(STATUS "Downloading Zig (espressif variant):")
|
||||||
|
message(STATUS " => ${ZIG_ARCHIVE}")
|
||||||
|
file(DOWNLOAD "${ZIG_URL}" "${ZIG_ARCHIVE}"
|
||||||
|
TLS_VERIFY ON
|
||||||
|
EXPECTED_HASH SHA256=${HASH_SUM}
|
||||||
|
STATUS download_status
|
||||||
|
LOG download_log
|
||||||
|
# SHOW_PROGRESS
|
||||||
|
)
|
||||||
|
list(GET download_status 0 dl_code)
|
||||||
|
if(NOT dl_code EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Download failed:\n${download_log}")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Extracting ${ARCHIVE_EXT} ...")
|
||||||
|
if(HOST_OS_LOWER MATCHES "windows|win")
|
||||||
|
execute_process(
|
||||||
|
COMMAND powershell -NoProfile -ExecutionPolicy Bypass
|
||||||
|
-Command "Expand-Archive -Path '${ZIG_ARCHIVE}' -DestinationPath '${CMAKE_BINARY_DIR}' -Force"
|
||||||
|
RESULT_VARIABLE extract_result
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E tar xf "${ZIG_ARCHIVE}"
|
||||||
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
|
||||||
|
RESULT_VARIABLE extract_result
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
if(NOT extract_result EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Extraction failed (code ${extract_result})")
|
||||||
|
endif()
|
||||||
|
file(REMOVE "${ZIG_ARCHIVE}")
|
||||||
|
else()
|
||||||
|
message(STATUS "Using cached espressif zig: ${ZIG_DIR}/zig")
|
||||||
|
endif()
|
||||||
|
set(ZIG_BIN "${ZIG_DIR}/zig")
|
||||||
63
software/zig_main/cmake/zig-runner.cmake
Normal file
63
software/zig_main/cmake/zig-runner.cmake
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
# HELPER to run zig cli (build - translate-c - fmt - ...)
|
||||||
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
function(zig_run)
|
||||||
|
cmake_parse_arguments(PARSE_ARGV 0 ARG
|
||||||
|
"VERBATIM;ALLOW_FAIL"
|
||||||
|
"WORKING_DIRECTORY;RESULT_VARIABLE;OUTPUT_VARIABLE;ERROR_VARIABLE;OUTPUT_FILE;TIMEOUT"
|
||||||
|
"COMMAND"
|
||||||
|
)
|
||||||
|
if(NOT ARG_COMMAND)
|
||||||
|
message(FATAL_ERROR "zig_run: COMMAND list is required")
|
||||||
|
endif()
|
||||||
|
if(NOT DEFINED ARG_WORKING_DIRECTORY)
|
||||||
|
set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
|
# Default timeout: 300 s. translate-c normally finishes in seconds;
|
||||||
|
if(NOT DEFINED ARG_TIMEOUT)
|
||||||
|
set(ARG_TIMEOUT 300)
|
||||||
|
endif()
|
||||||
|
set(extra_args)
|
||||||
|
if(ARG_VERBATIM)
|
||||||
|
list(APPEND extra_args VERBATIM)
|
||||||
|
endif()
|
||||||
|
if(ARG_OUTPUT_FILE)
|
||||||
|
list(APPEND extra_args OUTPUT_FILE "${ARG_OUTPUT_FILE}")
|
||||||
|
endif()
|
||||||
|
execute_process(
|
||||||
|
COMMAND "${ZIG_BIN}" ${ARG_COMMAND}
|
||||||
|
WORKING_DIRECTORY "${ARG_WORKING_DIRECTORY}"
|
||||||
|
RESULT_VARIABLE result
|
||||||
|
OUTPUT_VARIABLE output
|
||||||
|
ERROR_VARIABLE error
|
||||||
|
TIMEOUT ${ARG_TIMEOUT}
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_STRIP_TRAILING_WHITESPACE
|
||||||
|
${extra_args}
|
||||||
|
)
|
||||||
|
# Propagate results to parent scope if requested
|
||||||
|
if(DEFINED ARG_RESULT_VARIABLE)
|
||||||
|
set(${ARG_RESULT_VARIABLE} ${result} PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ARG_OUTPUT_VARIABLE)
|
||||||
|
set(${ARG_OUTPUT_VARIABLE} "${output}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(DEFINED ARG_ERROR_VARIABLE)
|
||||||
|
set(${ARG_ERROR_VARIABLE} "${error}" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
if(NOT ARG_ALLOW_FAIL)
|
||||||
|
if(result MATCHES "timeout")
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Zig command timed out after ${ARG_TIMEOUT}s\n"
|
||||||
|
" Command: ${ZIG_BIN} ${ARG_COMMAND}\n"
|
||||||
|
"--- stdout ---\n${output}\n"
|
||||||
|
"--- stderr ---\n${error}")
|
||||||
|
elseif(NOT result EQUAL 0)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Zig command failed (code ${result}):\n"
|
||||||
|
" ${ZIG_BIN} ${ARG_COMMAND}\n"
|
||||||
|
"--- stdout ---\n${output}\n"
|
||||||
|
"--- stderr ---\n${error}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
28621486f77229aaf81c71f5e15d6fbf36c2949cf11094e07090593e659e7639
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
## 3.0.3
|
||||||
|
|
||||||
|
- Support WS2816 with 16-bit color
|
||||||
|
|
||||||
|
## 3.0.1
|
||||||
|
|
||||||
|
- Support WS2811 bit timing
|
||||||
|
|
||||||
|
## 3.0.0
|
||||||
|
|
||||||
|
- Discontinued support for ESP-IDF v4.x
|
||||||
|
- Added configuration for user-defined color component format
|
||||||
|
|
||||||
|
## 2.5.5
|
||||||
|
|
||||||
|
- Simplified the led_strip component dependency, the time of full build with ESP-IDF v5.3 can now be shorter.
|
||||||
|
|
||||||
|
## 2.5.4
|
||||||
|
|
||||||
|
- Inserted extra delay when initialize the SPI LED device, to ensure all LEDs are in the reset state correctly
|
||||||
|
|
||||||
|
## 2.5.3
|
||||||
|
|
||||||
|
- Extend reset time (280us) to support WS2812B-V5
|
||||||
|
|
||||||
|
## 2.5.2
|
||||||
|
|
||||||
|
- Added API reference doc (api.md)
|
||||||
|
|
||||||
|
## 2.5.0
|
||||||
|
|
||||||
|
- Enabled support for IDF4.4 and above
|
||||||
|
- with RMT backend only
|
||||||
|
- Added API `led_strip_set_pixel_hsv`
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Support configurable SPI mode to control leds
|
||||||
|
- recommend enabling DMA when using SPI mode
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
- Support configurable RMT channel size by setting `mem_block_symbols`
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
|
||||||
|
- Support for 4 components RGBW leds (SK6812):
|
||||||
|
- in led_strip_config_t new fields
|
||||||
|
led_pixel_format, controlling byte format (LED_PIXEL_FORMAT_GRB, LED_PIXEL_FORMAT_GRBW)
|
||||||
|
led_model, used to configure bit timing (LED_MODEL_WS2812, LED_MODEL_SK6812)
|
||||||
|
- new API led_strip_set_pixel_rgbw
|
||||||
|
- new interface type set_pixel_rgbw
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
- Support DMA feature, which offloads the CPU by a lot when it comes to drive a bunch of LEDs
|
||||||
|
- Support various RMT clock sources
|
||||||
|
- Acquire and release the power management lock before and after each refresh
|
||||||
|
- New driver flag: `invert_out` which can invert the led control signal by hardware
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
- Reimplemented the driver using the new RMT driver (`driver/rmt_tx.h`)
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial driver version, based on the legacy RMT driver (`driver/rmt.h`)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"version":"1.0","algorithm":"sha256","created_at":"2026-02-04T21:14:01.842522+00:00","files":[{"path":"CHANGELOG.md","size":1667,"hash":"b445b45b8ce496848e247b569090efc3ea1a8680b8e69a7309dbdeeb97eb9d51"},{"path":"CMakeLists.txt","size":917,"hash":"038cbe6ba04c27101892e51d9d6a0627d64130f666f5d61b1f097462f982955b"},{"path":"LICENSE","size":11358,"hash":"cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"},{"path":"README.md","size":2072,"hash":"12e83a316c51d85c6c1ee2e5eecfb46691f6be42ce685eece2ce063a9c949001"},{"path":"idf_component.yml","size":492,"hash":"f4f09e02e4e53be5b49defeab1b6da6cdb9acfe377df892dfef5a0c370a7f88e"},{"path":"docs/Doxyfile","size":738,"hash":"7f64bdef18c3ed6f2e3d6397066e2fad4b5e31c2052744ca9631f34f69fdff79"},{"path":"docs/book.toml","size":297,"hash":"5d66624796168a4b8d0d87631c438c392b973206f4f7c53d9897a0b7ca7ce5b4"},{"path":"include/led_strip.h","size":4313,"hash":"36344aae936d7e0e764954188d77160c357d3ae1990ede1c7451e2bd1c0e85eb"},{"path":"include/led_strip_rmt.h","size":1630,"hash":"c63a152ab4aa187080b8d29cdb49365a9ea03b6ca7c41c66920e5c58ac0d0c52"},{"path":"include/led_strip_spi.h","size":1599,"hash":"cf0dcd5c748a7f11bf55077325b68a64ea826e55fc8e7b38aaad6fc0eb5345e5"},{"path":"include/led_strip_types.h","size":4276,"hash":"168b30b21ecbc5789903204753958c781304324e6a44d40a5fcb8676f12cb29c"},{"path":"interface/led_strip_interface.h","size":2934,"hash":"5b7d0c326d0d0d9748830d4aec46d765400e1446055d4a1197c83111e937d74c"},{"path":"src/led_strip_api.c","size":3841,"hash":"61968f0dde1cf1720f6b00bf3cb5d2c2b990aac6de00c90d9465746afc3e03d5"},{"path":"src/led_strip_rmt_dev.c","size":8925,"hash":"0e69d4743e65956ef2494d1347b81b99f283e0a79d6d341733fd1d58c1c6e97e"},{"path":"src/led_strip_rmt_encoder.c","size":8080,"hash":"4ab03dfbba4a90f392af3234665f76ccd21c004c61f5575e1e428354512d1748"},{"path":"src/led_strip_rmt_encoder.h","size":977,"hash":"690381c35ace2703a5c7156f6547a8524f4cbfe5bef40be619e2097960120a40"},{"path":"src/led_strip_spi_dev.c","size":11600,"hash":"79a4e4c42185afdbdf2163bd945a6339bf09965a85fb20525b3d7b106d695ab2"},{"path":"examples/led_strip_rmt_ws2812/CMakeLists.txt","size":140,"hash":"526f16308e57fafd25d0fd79d872152a9214c28967f78aa9c94ebe9e73040940"},{"path":"examples/led_strip_rmt_ws2812/README.md","size":1200,"hash":"a5f39b31c5f7cbf548ee31b61ab22e430a6c823404c0ddb113703512bcb3ad3c"},{"path":"examples/led_strip_spi_ws2812/CMakeLists.txt","size":140,"hash":"61255dc48f295f09e84abd7895ae5767763ac3decb4b4584e38681ea877427e8"},{"path":"examples/led_strip_spi_ws2812/README.md","size":1201,"hash":"2c02a29197cd1f2d4af4c4c9cd44677e303b0e168a1773eef9fc3fdb39377d27"},{"path":"examples/led_strip_spi_ws2812/main/CMakeLists.txt","size":99,"hash":"34e7f83d26bca924c629ea2012e6f200b415d486907863fe936d94872ff739eb"},{"path":"examples/led_strip_spi_ws2812/main/idf_component.yml","size":68,"hash":"a0c6b9b94056e8459a9acb8d7828540b36b4f7fe9ced9011ea97ba23b2fc96d4"},{"path":"examples/led_strip_spi_ws2812/main/led_strip_spi_ws2812_main.c","size":2808,"hash":"ef7ee688e7e1f451879a7b238b2a7133ccf880adb6d0e551328150acf86f656d"},{"path":"examples/led_strip_rmt_ws2812/main/CMakeLists.txt","size":99,"hash":"8960b68811805d3aa40e1a7f44ddf7400c0d0731829b6d2b3b1584d8dcd3b392"},{"path":"examples/led_strip_rmt_ws2812/main/idf_component.yml","size":53,"hash":"d52c7e09ecb7a6e4946fb6e697d6d7127918d4334858973f8c7434b1d2f120f0"},{"path":"examples/led_strip_rmt_ws2812/main/led_strip_rmt_ws2812_main.c","size":3253,"hash":"8835bd39d38dac8fb27c5e1298cb12ddf4c6ed430b4a2a1e061334f56d77f470"},{"path":"docs/src/SUMMARY.md","size":110,"hash":"b3a38ed25d2e5187928554682b1bd7154444e1bc1ce8183e6a3d328e720f7b61"},{"path":"docs/src/api.md","size":128,"hash":"d06c809c85c02f6ae22bd090331e1150dad89bd57034f056dbf3df0449cdc22b"},{"path":"docs/src/index.md","size":2967,"hash":"db944dabd24b1faa4d61a8f8db4f734334cefc2d1efb6d023a51fb94d1c3311f"}]}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||||
|
|
||||||
|
set(srcs "src/led_strip_api.c")
|
||||||
|
set(public_requires)
|
||||||
|
|
||||||
|
if(CONFIG_SOC_RMT_SUPPORTED)
|
||||||
|
list(APPEND srcs "src/led_strip_rmt_dev.c" "src/led_strip_rmt_encoder.c")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# the SPI backend driver relies on some feature that was available in IDF 5.1
|
||||||
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.1")
|
||||||
|
if(CONFIG_SOC_GPSPI_SUPPORTED)
|
||||||
|
list(APPEND srcs "src/led_strip_spi_dev.c")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Starting from esp-idf v5.3, the RMT and SPI drivers are moved to separate components
|
||||||
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||||
|
list(APPEND public_requires "esp_driver_rmt" "esp_driver_spi")
|
||||||
|
else()
|
||||||
|
list(APPEND public_requires "driver")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "include" "interface"
|
||||||
|
REQUIRES ${public_requires})
|
||||||
202
software/zig_main/components/espressif__led_strip/LICENSE
Normal file
202
software/zig_main/components/espressif__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.
|
||||||
23
software/zig_main/components/espressif__led_strip/README.md
Normal file
23
software/zig_main/components/espressif__led_strip/README.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# LED Strip Driver
|
||||||
|
|
||||||
|
[](https://components.espressif.com/components/espressif/led_strip)
|
||||||
|
|
||||||
|
This driver is designed for addressable LEDs like [WS2812](http://www.world-semi.com/Certifications/WS2812B.html), where each LED is controlled by a single data line.
|
||||||
|
|
||||||
|
## Supported Backend Peripherals
|
||||||
|
|
||||||
|
The LED strip driver supports two different backend peripherals to generate the timing signals required by addressable LEDs:
|
||||||
|
|
||||||
|
### The [RMT](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/rmt.html) Peripheral
|
||||||
|
|
||||||
|
This is the most economical way to drive the LEDs because it only consumes one RMT channel, leaving other channels free to use. However, the memory usage increases dramatically with the number of LEDs. If the RMT hardware can't be assist by DMA, the driver will going into interrupt very frequently, thus result in a high CPU usage. What's worse, if the RMT interrupt is delayed or not serviced in time (e.g. if Wi-Fi interrupt happens on the same CPU core), the RMT transaction will be corrupted and the LEDs will display incorrect colors. If you want to use RMT to drive a large number of LEDs, you'd better to enable the DMA feature if possible [^1].
|
||||||
|
|
||||||
|
### The [SPI](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/spi_master.html) Peripheral
|
||||||
|
|
||||||
|
SPI peripheral can also be used to generate the timing required by the LED strip, in a so-called "Clock-less" mode. However this backend is not as economical as the RMT one, because it will take up the whole **bus**. You **CANNOT** connect other devices to the same SPI bus if it's been used by the led_strip, because the led_strip doesn't have the concept of "Chip Select".
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
For detailed information about the LED Strip component, including API reference and user guides, please visit:
|
||||||
|
|
||||||
|
- **Programming Guide & API Reference**: [LED Strip Documentation](https://espressif.github.io/idf-extra-components/latest/led_strip/index.html)
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Set this to the header file you want
|
||||||
|
INPUT = \
|
||||||
|
../include/ \
|
||||||
|
../interface/
|
||||||
|
|
||||||
|
# The output directory for the generated XML documentation
|
||||||
|
OUTPUT_DIRECTORY = doxygen_output
|
||||||
|
|
||||||
|
# Warning-related settings, it's recommended to keep them enabled
|
||||||
|
WARN_IF_UNDOC_ENUM_VAL = YES
|
||||||
|
WARN_AS_ERROR = YES
|
||||||
|
|
||||||
|
# Other common settings
|
||||||
|
FULL_PATH_NAMES = YES
|
||||||
|
STRIP_FROM_PATH = ../
|
||||||
|
STRIP_FROM_INC_PATH = ../
|
||||||
|
ENABLE_PREPROCESSING = YES
|
||||||
|
MACRO_EXPANSION = YES
|
||||||
|
OPTIMIZE_OUTPUT_FOR_C = YES
|
||||||
|
EXPAND_ONLY_PREDEF = YES
|
||||||
|
EXTRACT_ALL = YES
|
||||||
|
PREDEFINED = $(ENV_DOXYGEN_DEFINES)
|
||||||
|
HAVE_DOT = NO
|
||||||
|
GENERATE_XML = YES
|
||||||
|
XML_OUTPUT = xml
|
||||||
|
GENERATE_HTML = NO
|
||||||
|
HAVE_DOT = NO
|
||||||
|
GENERATE_LATEX = NO
|
||||||
|
QUIET = YES
|
||||||
|
MARKDOWN_SUPPORT = YES
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
[book]
|
||||||
|
title = "LED Strip Documentation"
|
||||||
|
language = "en"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
default-theme = "light"
|
||||||
|
git-repository-url = "https://github.com/espressif/idf-extra-components/tree/master/led_strip"
|
||||||
|
edit-url-template = "https://github.com/espressif/idf-extra-components/edit/master/led_strip/docs/{path}"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Programming Guide
|
||||||
|
|
||||||
|
- [LED Strip](index.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
- [API Reference](api.md)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# API Reference
|
||||||
|
|
||||||
|
<div class="warning">
|
||||||
|
|
||||||
|
This file is automatically generated by esp-doxybook.
|
||||||
|
|
||||||
|
DO NOT edit it manually.
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# LED Strip Programming Guide
|
||||||
|
|
||||||
|
## Allocate LED Strip Object with RMT Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
/// LED strip common configuration
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = 1, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// RMT backend specific configuration
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.resolution_hz = 10 * 1000 * 1000, // RMT counter clock frequency: 10MHz
|
||||||
|
.mem_block_symbols = 64, // the memory size of each RMT channel, in words (4 bytes)
|
||||||
|
.flags = {
|
||||||
|
.with_dma = false, // DMA feature is available on chips like ESP32-S3/P4
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create the LED strip object
|
||||||
|
led_strip_handle_t led_strip = NULL;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
You can create multiple LED strip objects with different GPIOs and pixel numbers. The backend driver will automatically allocate sufficient RMT channels for you wherever possible. If the RMT channels are not enough, the [led_strip_new_rmt_device](api.md#function-led_strip_new_rmt_device) will return an error.
|
||||||
|
|
||||||
|
## Allocate LED Strip Object with SPI Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
/// LED strip common configuration
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = 1, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model, it determines the bit timing
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color component format is G-R-B
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// SPI backend specific configuration
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
.flags = {
|
||||||
|
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Create the LED strip object
|
||||||
|
led_strip_handle_t led_strip = NULL;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The number of LED strip objects can be created depends on how many free SPI controllers are free to use in your project.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
- How to set the brightness of the LED strip?
|
||||||
|
- You can tune the brightness by scaling the value of each R-G-B element with a **same** factor. But pay attention to the overflow of the value.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(led_strip_rmt_ws2812)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# LED Strip Example (RMT backend + WS2812)
|
||||||
|
|
||||||
|
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with Espressif SoC
|
||||||
|
* A USB cable for Power supply and programming
|
||||||
|
* WS2812 LED strip
|
||||||
|
|
||||||
|
### Configure the Example
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_rmt_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
I (299) gpio: GPIO[8]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||||
|
I (309) example: Created LED strip object with RMT backend
|
||||||
|
I (309) example: Start blinking LED strip
|
||||||
|
```
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "led_strip_rmt_ws2812_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: ^3
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
// Set to 1 to use DMA for driving the LED strip, 0 otherwise
|
||||||
|
// Please note the RMT DMA feature is only available on chips e.g. ESP32-S3/P4
|
||||||
|
#define LED_STRIP_USE_DMA 0
|
||||||
|
|
||||||
|
#if LED_STRIP_USE_DMA
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 256
|
||||||
|
#define LED_STRIP_MEMORY_BLOCK_WORDS 1024 // this determines the DMA block size
|
||||||
|
#else
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 24
|
||||||
|
#define LED_STRIP_MEMORY_BLOCK_WORDS 0 // let the driver choose a proper memory block size automatically
|
||||||
|
#endif // LED_STRIP_USE_DMA
|
||||||
|
|
||||||
|
// GPIO assignment
|
||||||
|
#define LED_STRIP_GPIO_PIN 2
|
||||||
|
|
||||||
|
// 10MHz resolution, 1 tick = 0.1us (led strip needs a high resolution)
|
||||||
|
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
led_strip_handle_t configure_led(void)
|
||||||
|
{
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.color_component_format = LED_STRIP_COLOR_COMPONENT_FMT_GRB, // The color order of the strip: GRB
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED strip backend configuration: RMT
|
||||||
|
led_strip_rmt_config_t rmt_config = {
|
||||||
|
.clk_src = RMT_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.resolution_hz = LED_STRIP_RMT_RES_HZ, // RMT counter clock frequency
|
||||||
|
.mem_block_symbols = LED_STRIP_MEMORY_BLOCK_WORDS, // the memory block size used by the RMT channel
|
||||||
|
.flags = {
|
||||||
|
.with_dma = LED_STRIP_USE_DMA, // Using DMA can improve performance when driving more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED Strip object handle
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
ESP_LOGI(TAG, "Created LED strip object with RMT backend");
|
||||||
|
return led_strip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
led_strip_handle_t led_strip = configure_led();
|
||||||
|
bool led_on_off = false;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||||
|
while (1) {
|
||||||
|
if (led_on_off) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||||
|
}
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED ON!");
|
||||||
|
} else {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED OFF!");
|
||||||
|
}
|
||||||
|
|
||||||
|
led_on_off = !led_on_off;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(led_strip_spi_ws2812)
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# LED Strip Example (SPI backend + WS2812)
|
||||||
|
|
||||||
|
This example demonstrates how to blink the WS2812 LED using the [led_strip](https://components.espressif.com/component/espressif/led_strip) component.
|
||||||
|
|
||||||
|
## How to Use Example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* A development board with Espressif SoC
|
||||||
|
* A USB cable for Power supply and programming
|
||||||
|
* WS2812 LED strip
|
||||||
|
|
||||||
|
### Configure the Example
|
||||||
|
|
||||||
|
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`. Then assign the proper GPIO in the [source file](main/led_strip_spi_ws2812_main.c). If your led strip has multiple LEDs, don't forget update the number.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
## Example Output
|
||||||
|
|
||||||
|
```text
|
||||||
|
I (299) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||||
|
I (309) example: Created LED strip object with SPI backend
|
||||||
|
I (309) example: Start blinking LED strip
|
||||||
|
```
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "led_strip_spi_ws2812_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: ^3
|
||||||
|
idf: '>=5.1'
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
// GPIO assignment
|
||||||
|
#define LED_STRIP_GPIO_PIN 2
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_COUNT 24
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
led_strip_handle_t configure_led(void)
|
||||||
|
{
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = LED_STRIP_GPIO_PIN, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_COUNT, // The number of LEDs in the strip,
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
// set the color order of the strip: GRB
|
||||||
|
.color_component_format = {
|
||||||
|
.format = {
|
||||||
|
.r_pos = 1, // red is the second byte in the color data
|
||||||
|
.g_pos = 0, // green is the first byte in the color data
|
||||||
|
.b_pos = 2, // blue is the third byte in the color data
|
||||||
|
.num_components = 3, // total 3 color components
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.invert_out = false, // don't invert the output signal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED strip backend configuration: SPI
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
.flags = {
|
||||||
|
.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED Strip object handle
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
|
||||||
|
ESP_LOGI(TAG, "Created LED strip object with SPI backend");
|
||||||
|
return led_strip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
led_strip_handle_t led_strip = configure_led();
|
||||||
|
bool led_on_off = false;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Start blinking LED strip");
|
||||||
|
while (1) {
|
||||||
|
if (led_on_off) {
|
||||||
|
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
|
||||||
|
for (int i = 0; i < LED_STRIP_LED_COUNT; i++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i, 5, 5, 5));
|
||||||
|
}
|
||||||
|
/* Refresh the strip to send data */
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED ON!");
|
||||||
|
} else {
|
||||||
|
/* Set all LED off to clear all pixels */
|
||||||
|
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||||
|
ESP_LOGI(TAG, "LED OFF!");
|
||||||
|
}
|
||||||
|
|
||||||
|
led_on_off = !led_on_off;
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
dependencies:
|
||||||
|
idf: '>=5.0'
|
||||||
|
description: Driver for Addressable LED Strip (WS2812, etc)
|
||||||
|
documentation: https://espressif.github.io/idf-extra-components/latest/led_strip/index.html
|
||||||
|
issues: https://github.com/espressif/idf-extra-components/issues
|
||||||
|
repository: git://github.com/espressif/idf-extra-components.git
|
||||||
|
repository_info:
|
||||||
|
commit_sha: 7cd447361ca2f0a1c01aa3089e3031f6171b6c7e
|
||||||
|
path: led_strip
|
||||||
|
url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
|
||||||
|
version: 3.0.3
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "led_strip_rmt.h"
|
||||||
|
#include "led_strip_spi.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGB for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||||
|
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGBW for a specific pixel
|
||||||
|
*
|
||||||
|
* @note Only call this function if your led strip does have the white component (e.g. SK6812-RGBW)
|
||||||
|
* @note Also see `led_strip_set_pixel` if you only want to specify the RGB part of the color and bypass the white component
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
* @param white: separate white component
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set HSV for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param hue: hue part of color (0 - 360)
|
||||||
|
* @param saturation: saturation part of color (0 - 255, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 127)
|
||||||
|
* @param value: value part of color (0 - 255, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 127)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set HSV for a specific pixel in 16-bit resolution
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param hue: hue part of color (0 - 360)
|
||||||
|
* @param saturation: saturation part of color (0 - 65535, rescaled from 0 - 1. e.g. saturation = 0.5, rescaled to 32767)
|
||||||
|
* @param value: value part of color (0 - 65535, rescaled from 0 - 1. e.g. value = 0.5, rescaled to 32767)
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set HSV color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set HSV color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set HSV color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Refresh memory colors to LEDs
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Refresh successfully
|
||||||
|
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||||
|
*
|
||||||
|
* @note:
|
||||||
|
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_refresh(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear LED strip (turn off all LEDs)
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Clear LEDs successfully
|
||||||
|
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_clear(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free LED strip resources
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Free resources successfully
|
||||||
|
* - ESP_FAIL: Free resources failed because error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_del(led_strip_handle_t strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "led_strip_types.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#include "driver/rmt_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip RMT specific configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
rmt_clock_source_t clk_src; /*!< RMT clock source */
|
||||||
|
uint32_t resolution_hz; /*!< RMT tick resolution, if set to zero, a default resolution (10MHz) will be applied */
|
||||||
|
size_t mem_block_symbols; /*!< How many RMT symbols can one RMT channel hold at one time. Set to 0 will fallback to use the default size. */
|
||||||
|
/*!< Extra RMT specific driver flags */
|
||||||
|
struct led_strip_rmt_extra_config {
|
||||||
|
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_rmt_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LED strip based on RMT TX channel
|
||||||
|
*
|
||||||
|
* @param led_config LED strip configuration
|
||||||
|
* @param rmt_config RMT specific configuration
|
||||||
|
* @param ret_strip Returned LED strip handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: create LED strip handle successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||||
|
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||||
|
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_new_rmt_device(const led_strip_config2_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "led_strip_types.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip SPI specific configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
spi_clock_source_t clk_src; /*!< SPI clock source */
|
||||||
|
spi_host_device_t spi_bus; /*!< SPI bus ID. Which buses are available depends on the specific chip */
|
||||||
|
struct {
|
||||||
|
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_spi_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create LED strip based on SPI MOSI channel
|
||||||
|
*
|
||||||
|
* @note Although only the MOSI line is used for generating the signal, the whole SPI bus can't be used for other purposes.
|
||||||
|
*
|
||||||
|
* @param led_config LED strip configuration
|
||||||
|
* @param spi_config SPI specific configuration
|
||||||
|
* @param ret_strip Returned LED strip handle
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: create LED strip handle successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: create LED strip handle failed because of invalid argument
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED: create LED strip handle failed because of unsupported configuration
|
||||||
|
* - ESP_ERR_NO_MEM: create LED strip handle failed because of out of memory
|
||||||
|
* - ESP_FAIL: create LED strip handle failed because some other error
|
||||||
|
*/
|
||||||
|
esp_err_t led_strip_new_spi_device(const led_strip_config2_t *led_config, const led_strip_spi_config_t *spi_config, led_strip_handle_t *ret_strip);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type of LED strip handle
|
||||||
|
*/
|
||||||
|
typedef struct led_strip_t *led_strip_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip model
|
||||||
|
* @note Different led model may have different timing parameters, so we need to distinguish them.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LED_MODEL_WS2812, /*!< LED strip model: WS2812 */
|
||||||
|
LED_MODEL_SK6812, /*!< LED strip model: SK6812 */
|
||||||
|
LED_MODEL_WS2811, /*!< LED strip model: WS2811 */
|
||||||
|
LED_MODEL_WS2816, /*!< LED strip model: WS2816 */
|
||||||
|
LED_MODEL_INVALID /*!< Invalid LED strip model */
|
||||||
|
} led_model_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED color component format
|
||||||
|
* @note The format is used to specify the order of color components in each pixel, also the number of color components.
|
||||||
|
*/
|
||||||
|
typedef union {
|
||||||
|
struct format_layout {
|
||||||
|
uint32_t r_pos: 2; /*!< Position of the red channel in the color order: 0~3 */
|
||||||
|
uint32_t g_pos: 2; /*!< Position of the green channel in the color order: 0~3 */
|
||||||
|
uint32_t b_pos: 2; /*!< Position of the blue channel in the color order: 0~3 */
|
||||||
|
uint32_t w_pos: 2; /*!< Position of the white channel in the color order: 0~3 */
|
||||||
|
uint32_t reserved: 19; /*!< Reserved */
|
||||||
|
uint32_t bytes_per_color: 2; /*!< Bytes per color component: 1 or 2. If set to 0, it will fallback to 1 */
|
||||||
|
uint32_t num_components: 3; /*!< Number of color components per pixel: 3 or 4. If set to 0, it will fallback to 3 */
|
||||||
|
} format; /*!< Format layout */
|
||||||
|
uint32_t format_id; /*!< Format ID */
|
||||||
|
} led_color_component_format_t;
|
||||||
|
|
||||||
|
/// Helper macros to set the color component format
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRB_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_GRBW_16 (led_color_component_format_t){.format = {.r_pos = 1, .g_pos = 0, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGB_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 3}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 1, .num_components = 4}}
|
||||||
|
#define LED_STRIP_COLOR_COMPONENT_FMT_RGBW_16 (led_color_component_format_t){.format = {.r_pos = 0, .g_pos = 1, .b_pos = 2, .w_pos = 3, .reserved = 0, .bytes_per_color = 2, .num_components = 4}}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip common configurations
|
||||||
|
* The common configurations are not specific to any backend peripheral.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||||
|
uint32_t max_leds; /*!< Maximum number of LEDs that can be controlled in a single strip */
|
||||||
|
led_model_t led_model; /*!< Specifies the LED strip model (e.g., WS2812, SK6812) */
|
||||||
|
led_color_component_format_t color_component_format; /*!< Specifies the order of color components in each pixel.
|
||||||
|
Use helper macros like `LED_STRIP_COLOR_COMPONENT_FMT_GRB` to set the format */
|
||||||
|
/*!< LED strip extra driver flags */
|
||||||
|
struct led_strip_extra_flags {
|
||||||
|
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||||
|
} flags; /*!< Extra driver flags */
|
||||||
|
} led_strip_config2_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct led_strip_t led_strip_t; /*!< Type of LED strip */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip interface definition
|
||||||
|
*/
|
||||||
|
struct led_strip_t {
|
||||||
|
/**
|
||||||
|
* @brief Set RGB for a specific pixel
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGB for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGB for a specific pixel failed because of invalid parameters
|
||||||
|
* - ESP_FAIL: Set RGB for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set RGBW for a specific pixel. Similar to `set_pixel` but also set the white component
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param index: index of pixel to set
|
||||||
|
* @param red: red part of color
|
||||||
|
* @param green: green part of color
|
||||||
|
* @param blue: blue part of color
|
||||||
|
* @param white: separate white component
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Set RGBW color for a specific pixel successfully
|
||||||
|
* - ESP_ERR_INVALID_ARG: Set RGBW color for a specific pixel failed because of an invalid argument
|
||||||
|
* - ESP_FAIL: Set RGBW color for a specific pixel failed because other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*set_pixel_rgbw)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Refresh memory colors to LEDs
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param timeout_ms: timeout value for refreshing task
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Refresh successfully
|
||||||
|
* - ESP_FAIL: Refresh failed because some other error occurred
|
||||||
|
*
|
||||||
|
* @note:
|
||||||
|
* After updating the LED colors in the memory, a following invocation of this API is needed to flush colors to strip.
|
||||||
|
*/
|
||||||
|
esp_err_t (*refresh)(led_strip_t *strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear LED strip (turn off all LEDs)
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
* @param timeout_ms: timeout value for clearing task
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Clear LEDs successfully
|
||||||
|
* - ESP_FAIL: Clear LEDs failed because some other error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*clear)(led_strip_t *strip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free LED strip resources
|
||||||
|
*
|
||||||
|
* @param strip: LED strip
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK: Free resources successfully
|
||||||
|
* - ESP_FAIL: Free resources failed because error occurred
|
||||||
|
*/
|
||||||
|
esp_err_t (*del)(led_strip_t *strip);
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "led_strip_interface.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_strip";
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_hsv(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint8_t saturation, uint8_t value)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
|
||||||
|
uint32_t red = 0;
|
||||||
|
uint32_t green = 0;
|
||||||
|
uint32_t blue = 0;
|
||||||
|
|
||||||
|
uint32_t rgb_max = value;
|
||||||
|
uint32_t rgb_min = rgb_max * (255 - saturation) / 255;
|
||||||
|
|
||||||
|
uint32_t i = hue / 60;
|
||||||
|
uint32_t diff = hue % 60;
|
||||||
|
|
||||||
|
// RGB adjustment amount by hue
|
||||||
|
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min + rgb_adj;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
red = rgb_max - rgb_adj;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min + rgb_adj;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max - rgb_adj;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
red = rgb_min + rgb_adj;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max - rgb_adj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_hsv_16(led_strip_handle_t strip, uint32_t index, uint16_t hue, uint16_t saturation, uint16_t value)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
|
||||||
|
uint32_t red = 0;
|
||||||
|
uint32_t green = 0;
|
||||||
|
uint32_t blue = 0;
|
||||||
|
|
||||||
|
uint32_t rgb_max = value;
|
||||||
|
uint32_t rgb_min = rgb_max * (65535 - saturation) / 65535;
|
||||||
|
|
||||||
|
uint32_t i = hue / 60;
|
||||||
|
uint32_t diff = hue % 60;
|
||||||
|
|
||||||
|
// RGB adjustment amount by hue
|
||||||
|
uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
|
||||||
|
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min + rgb_adj;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
red = rgb_max - rgb_adj;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max;
|
||||||
|
blue = rgb_min + rgb_adj;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
red = rgb_min;
|
||||||
|
green = rgb_max - rgb_adj;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
red = rgb_min + rgb_adj;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
red = rgb_max;
|
||||||
|
green = rgb_min;
|
||||||
|
blue = rgb_max - rgb_adj;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strip->set_pixel(strip, index, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_set_pixel_rgbw(led_strip_handle_t strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->set_pixel_rgbw(strip, index, red, green, blue, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_refresh(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->refresh(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_clear(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->clear(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_del(led_strip_handle_t strip)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(strip, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
|
return strip->del(strip);
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/rmt_tx.h"
|
||||||
|
#include "led_strip.h"
|
||||||
|
#include "led_strip_interface.h"
|
||||||
|
#include "led_strip_rmt_encoder.h"
|
||||||
|
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_RESOLUTION 10000000 // 10MHz resolution
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE 4
|
||||||
|
// the memory size of each RMT channel, in words (4 bytes)
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 64
|
||||||
|
#else
|
||||||
|
#define LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS 48
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "led_strip_rmt";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
led_strip_t base;
|
||||||
|
rmt_channel_handle_t rmt_chan;
|
||||||
|
rmt_encoder_handle_t strip_encoder;
|
||||||
|
uint32_t strip_len;
|
||||||
|
uint8_t bytes_per_pixel;
|
||||||
|
led_color_component_format_t component_fmt;
|
||||||
|
uint8_t pixel_buf[];
|
||||||
|
} led_strip_rmt_obj;
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_set_pixel(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
|
||||||
|
struct format_layout format = rmt_strip->component_fmt.format;
|
||||||
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||||
|
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||||
|
if (format.num_components > 3) {
|
||||||
|
pixel_buf[start + format.w_pos * pos_bytes + i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_set_pixel_rgbw(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue, uint32_t white)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
struct format_layout format = rmt_strip->component_fmt.format;
|
||||||
|
ESP_RETURN_ON_FALSE(index < rmt_strip->strip_len, ESP_ERR_INVALID_ARG, TAG, "index out of maximum number of LEDs");
|
||||||
|
ESP_RETURN_ON_FALSE(format.num_components == 4, ESP_ERR_INVALID_ARG, TAG, "led doesn't have 4 components");
|
||||||
|
|
||||||
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||||
|
uint8_t *pixel_buf = rmt_strip->pixel_buf;
|
||||||
|
uint8_t pos_bytes = format.bytes_per_color;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < format.bytes_per_color; i++) {
|
||||||
|
uint8_t color_shift = 8 * (format.bytes_per_color - 1 - i);
|
||||||
|
pixel_buf[start + format.r_pos * pos_bytes + i] = (red >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.g_pos * pos_bytes + i] = (green >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.b_pos * pos_bytes + i] = (blue >> color_shift) & 0xFF;
|
||||||
|
pixel_buf[start + format.w_pos * pos_bytes + i] = (white >> color_shift) & 0xFF;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_refresh(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
rmt_transmit_config_t tx_conf = {
|
||||||
|
.loop_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_enable(rmt_strip->rmt_chan), TAG, "enable RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_transmit(rmt_strip->rmt_chan, rmt_strip->strip_encoder, rmt_strip->pixel_buf,
|
||||||
|
rmt_strip->strip_len * rmt_strip->bytes_per_pixel, &tx_conf), TAG, "transmit pixels by RMT failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_tx_wait_all_done(rmt_strip->rmt_chan, -1), TAG, "flush RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_disable(rmt_strip->rmt_chan), TAG, "disable RMT channel failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_clear(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
// Write zero to turn off all leds
|
||||||
|
memset(rmt_strip->pixel_buf, 0, rmt_strip->strip_len * rmt_strip->bytes_per_pixel);
|
||||||
|
return led_strip_rmt_refresh(strip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t led_strip_rmt_del(led_strip_t *strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = __containerof(strip, led_strip_rmt_obj, base);
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_del_channel(rmt_strip->rmt_chan), TAG, "delete RMT channel failed");
|
||||||
|
ESP_RETURN_ON_ERROR(rmt_del_encoder(rmt_strip->strip_encoder), TAG, "delete strip encoder failed");
|
||||||
|
free(rmt_strip);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t led_strip_new_rmt_device(const led_strip_config2_t *led_config, const led_strip_rmt_config_t *rmt_config, led_strip_handle_t *ret_strip)
|
||||||
|
{
|
||||||
|
led_strip_rmt_obj *rmt_strip = NULL;
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
ESP_GOTO_ON_FALSE(led_config && rmt_config && ret_strip, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
led_color_component_format_t component_fmt = led_config->color_component_format;
|
||||||
|
// If R/G/B order is not specified, set default GRB order as fallback
|
||||||
|
if (component_fmt.format_id == 0) {
|
||||||
|
component_fmt = LED_STRIP_COLOR_COMPONENT_FMT_GRB;
|
||||||
|
}
|
||||||
|
if (led_config->led_model == LED_MODEL_WS2816) {
|
||||||
|
component_fmt.format.bytes_per_color = 2;
|
||||||
|
}
|
||||||
|
if (component_fmt.format.bytes_per_color == 0) {
|
||||||
|
component_fmt.format.bytes_per_color = 1;
|
||||||
|
}
|
||||||
|
// check the validation of the color component format
|
||||||
|
uint8_t mask = 0;
|
||||||
|
if (component_fmt.format.num_components == 3) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x07, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else if (component_fmt.format.num_components == 4) {
|
||||||
|
mask = BIT(component_fmt.format.r_pos) | BIT(component_fmt.format.g_pos) | BIT(component_fmt.format.b_pos) | BIT(component_fmt.format.w_pos);
|
||||||
|
// Check for invalid values
|
||||||
|
ESP_RETURN_ON_FALSE(mask == 0x0F, ESP_ERR_INVALID_ARG, TAG, "invalid order argument");
|
||||||
|
} else {
|
||||||
|
ESP_RETURN_ON_FALSE(false, ESP_ERR_INVALID_ARG, TAG, "invalid number of color components: %d", component_fmt.format.num_components);
|
||||||
|
}
|
||||||
|
uint8_t bytes_per_pixel = component_fmt.format.num_components;
|
||||||
|
if (component_fmt.format.bytes_per_color > 1) {
|
||||||
|
bytes_per_pixel *= component_fmt.format.bytes_per_color;
|
||||||
|
}
|
||||||
|
rmt_strip = calloc(1, sizeof(led_strip_rmt_obj) + led_config->max_leds * bytes_per_pixel);
|
||||||
|
ESP_GOTO_ON_FALSE(rmt_strip, ESP_ERR_NO_MEM, err, TAG, "no mem for rmt strip");
|
||||||
|
uint32_t resolution = rmt_config->resolution_hz ? rmt_config->resolution_hz : LED_STRIP_RMT_DEFAULT_RESOLUTION;
|
||||||
|
|
||||||
|
// for backward compatibility, if the user does not set the clk_src, use the default value
|
||||||
|
rmt_clock_source_t clk_src = RMT_CLK_SRC_DEFAULT;
|
||||||
|
if (rmt_config->clk_src) {
|
||||||
|
clk_src = rmt_config->clk_src;
|
||||||
|
}
|
||||||
|
size_t mem_block_symbols = LED_STRIP_RMT_DEFAULT_MEM_BLOCK_SYMBOLS;
|
||||||
|
// override the default value if the user sets it
|
||||||
|
if (rmt_config->mem_block_symbols) {
|
||||||
|
mem_block_symbols = rmt_config->mem_block_symbols;
|
||||||
|
}
|
||||||
|
rmt_tx_channel_config_t rmt_chan_config = {
|
||||||
|
.clk_src = clk_src,
|
||||||
|
.gpio_num = led_config->strip_gpio_num,
|
||||||
|
.mem_block_symbols = mem_block_symbols,
|
||||||
|
.resolution_hz = resolution,
|
||||||
|
.trans_queue_depth = LED_STRIP_RMT_DEFAULT_TRANS_QUEUE_SIZE,
|
||||||
|
.flags.with_dma = rmt_config->flags.with_dma,
|
||||||
|
.flags.invert_out = led_config->flags.invert_out,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_tx_channel(&rmt_chan_config, &rmt_strip->rmt_chan), err, TAG, "create RMT TX channel failed");
|
||||||
|
|
||||||
|
led_strip_encoder_config_t strip_encoder_conf = {
|
||||||
|
.resolution = resolution,
|
||||||
|
.led_model = led_config->led_model
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_led_strip_encoder(&strip_encoder_conf, &rmt_strip->strip_encoder), err, TAG, "create LED strip encoder failed");
|
||||||
|
|
||||||
|
rmt_strip->component_fmt = component_fmt;
|
||||||
|
rmt_strip->bytes_per_pixel = bytes_per_pixel;
|
||||||
|
rmt_strip->strip_len = led_config->max_leds;
|
||||||
|
rmt_strip->base.set_pixel = led_strip_rmt_set_pixel;
|
||||||
|
rmt_strip->base.set_pixel_rgbw = led_strip_rmt_set_pixel_rgbw;
|
||||||
|
rmt_strip->base.refresh = led_strip_rmt_refresh;
|
||||||
|
rmt_strip->base.clear = led_strip_rmt_clear;
|
||||||
|
rmt_strip->base.del = led_strip_rmt_del;
|
||||||
|
|
||||||
|
*ret_strip = &rmt_strip->base;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
if (rmt_strip) {
|
||||||
|
if (rmt_strip->rmt_chan) {
|
||||||
|
rmt_del_channel(rmt_strip->rmt_chan);
|
||||||
|
}
|
||||||
|
if (rmt_strip->strip_encoder) {
|
||||||
|
rmt_del_encoder(rmt_strip->strip_encoder);
|
||||||
|
}
|
||||||
|
free(rmt_strip);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "esp_attr.h"
|
||||||
|
#include "led_strip_rmt_encoder.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_rmt_encoder";
|
||||||
|
|
||||||
|
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0)
|
||||||
|
#if CONFIG_RMT_ISR_IRAM_SAFE
|
||||||
|
#define RMT_ENCODER_FUNC_ATTR IRAM_ATTR
|
||||||
|
#else
|
||||||
|
#define RMT_ENCODER_FUNC_ATTR
|
||||||
|
#endif // CONFIG_RMT_ISR_IRAM_SAFE
|
||||||
|
#endif // ESP_IDF_VERSION
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
rmt_encoder_t base;
|
||||||
|
rmt_encoder_t *bytes_encoder;
|
||||||
|
rmt_encoder_t *copy_encoder;
|
||||||
|
int state;
|
||||||
|
rmt_symbol_word_t reset_code;
|
||||||
|
} rmt_led_strip_encoder_t;
|
||||||
|
|
||||||
|
RMT_ENCODER_FUNC_ATTR
|
||||||
|
static size_t rmt_encode_led_strip(rmt_encoder_t *encoder, rmt_channel_handle_t channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_encoder_handle_t bytes_encoder = led_encoder->bytes_encoder;
|
||||||
|
rmt_encoder_handle_t copy_encoder = led_encoder->copy_encoder;
|
||||||
|
rmt_encode_state_t session_state = 0;
|
||||||
|
rmt_encode_state_t state = 0;
|
||||||
|
size_t encoded_symbols = 0;
|
||||||
|
switch (led_encoder->state) {
|
||||||
|
case 0: // send RGB data
|
||||||
|
encoded_symbols += bytes_encoder->encode(bytes_encoder, channel, primary_data, data_size, &session_state);
|
||||||
|
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||||
|
led_encoder->state = 1; // switch to next state when current encoding session finished
|
||||||
|
}
|
||||||
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||||
|
state |= RMT_ENCODING_MEM_FULL;
|
||||||
|
goto out; // yield if there's no free space for encoding artifacts
|
||||||
|
}
|
||||||
|
// fall-through
|
||||||
|
case 1: // send reset code
|
||||||
|
encoded_symbols += copy_encoder->encode(copy_encoder, channel, &led_encoder->reset_code,
|
||||||
|
sizeof(led_encoder->reset_code), &session_state);
|
||||||
|
if (session_state & RMT_ENCODING_COMPLETE) {
|
||||||
|
led_encoder->state = 0; // back to the initial encoding session
|
||||||
|
state |= RMT_ENCODING_COMPLETE;
|
||||||
|
}
|
||||||
|
if (session_state & RMT_ENCODING_MEM_FULL) {
|
||||||
|
state |= RMT_ENCODING_MEM_FULL;
|
||||||
|
goto out; // yield if there's no free space for encoding artifacts
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
*ret_state = state;
|
||||||
|
return encoded_symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t rmt_del_led_strip_encoder(rmt_encoder_t *encoder)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||||
|
rmt_del_encoder(led_encoder->copy_encoder);
|
||||||
|
free(led_encoder);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RMT_ENCODER_FUNC_ATTR
|
||||||
|
static esp_err_t rmt_led_strip_encoder_reset(rmt_encoder_t *encoder)
|
||||||
|
{
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = __containerof(encoder, rmt_led_strip_encoder_t, base);
|
||||||
|
rmt_encoder_reset(led_encoder->bytes_encoder);
|
||||||
|
rmt_encoder_reset(led_encoder->copy_encoder);
|
||||||
|
led_encoder->state = 0;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t rmt_new_led_strip_encoder(const led_strip_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
rmt_led_strip_encoder_t *led_encoder = NULL;
|
||||||
|
ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||||
|
ESP_GOTO_ON_FALSE(config->led_model < LED_MODEL_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led model");
|
||||||
|
led_encoder = calloc(1, sizeof(rmt_led_strip_encoder_t));
|
||||||
|
ESP_GOTO_ON_FALSE(led_encoder, ESP_ERR_NO_MEM, err, TAG, "no mem for led strip encoder");
|
||||||
|
led_encoder->base.encode = rmt_encode_led_strip;
|
||||||
|
led_encoder->base.del = rmt_del_led_strip_encoder;
|
||||||
|
led_encoder->base.reset = rmt_led_strip_encoder_reset;
|
||||||
|
rmt_bytes_encoder_config_t bytes_encoder_config;
|
||||||
|
uint32_t reset_ticks = config->resolution / 1000000 * 280 / 2; // reset code duration defaults to 280us to accommodate WS2812B-V5
|
||||||
|
if (config->led_model == LED_MODEL_SK6812) {
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.6 * config->resolution / 1000000, // T1H=0.6us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.6 * config->resolution / 1000000, // T1L=0.6us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1 // SK6812 transfer bit order: G7...G0R7...R0B7...B0(W7...W0)
|
||||||
|
};
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2812) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2812
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.9 * config->resolution / 1000000, // T0L=0.9us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.9 * config->resolution / 1000000, // T1H=0.9us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.3 * config->resolution / 1000000, // T1L=0.3us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1 // WS2812 transfer bit order: G7...G0R7...R0B7...B0
|
||||||
|
};
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2811) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2811
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.5 * config->resolution / 1000000., // T0H=0.5us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 2.0 * config->resolution / 1000000., // T0L=2.0us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 1.2 * config->resolution / 1000000., // T1H=1.2us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 1.3 * config->resolution / 1000000., // T1L=1.3us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1
|
||||||
|
};
|
||||||
|
reset_ticks = config->resolution / 1000000 * 50 / 2; // divide by 2... signal is sent twice
|
||||||
|
} else if (config->led_model == LED_MODEL_WS2816) {
|
||||||
|
// different led strip might have its own timing requirements, following parameter is for WS2816
|
||||||
|
bytes_encoder_config = (rmt_bytes_encoder_config_t) {
|
||||||
|
.bit0 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.3 * config->resolution / 1000000, // T0H=0.3us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.95 * config->resolution / 1000000, // T0L=0.95us
|
||||||
|
},
|
||||||
|
.bit1 = {
|
||||||
|
.level0 = 1,
|
||||||
|
.duration0 = 0.75 * config->resolution / 1000000, // T1H=0.75us
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = 0.5 * config->resolution / 1000000, // T1L=0.5us
|
||||||
|
},
|
||||||
|
.flags.msb_first = 1
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config, &led_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
|
||||||
|
rmt_copy_encoder_config_t copy_encoder_config = {};
|
||||||
|
ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(©_encoder_config, &led_encoder->copy_encoder), err, TAG, "create copy encoder failed");
|
||||||
|
|
||||||
|
led_encoder->reset_code = (rmt_symbol_word_t) {
|
||||||
|
.level0 = 0,
|
||||||
|
.duration0 = reset_ticks,
|
||||||
|
.level1 = 0,
|
||||||
|
.duration1 = reset_ticks,
|
||||||
|
};
|
||||||
|
*ret_encoder = &led_encoder->base;
|
||||||
|
return ESP_OK;
|
||||||
|
err:
|
||||||
|
if (led_encoder) {
|
||||||
|
if (led_encoder->bytes_encoder) {
|
||||||
|
rmt_del_encoder(led_encoder->bytes_encoder);
|
||||||
|
}
|
||||||
|
if (led_encoder->copy_encoder) {
|
||||||
|
rmt_del_encoder(led_encoder->copy_encoder);
|
||||||
|
}
|
||||||
|
free(led_encoder);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user