mirror of
https://gitlab.com/fabinfra/fabhardware/fablight.git
synced 2025-03-12 22:51:47 +01:00
Init Firmware
This commit is contained in:
parent
8f99d0ec05
commit
0353727c8d
48
firmware/.devcontainer/Dockerfile
Normal file
48
firmware/.devcontainer/Dockerfile
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
FROM espressif/idf
|
||||||
|
|
||||||
|
ARG DEBIAN_FRONTEND=nointeractive
|
||||||
|
ARG CONTAINER_USER=esp
|
||||||
|
ARG USER_UID=1000
|
||||||
|
ARG USER_GID=$USER_UID
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt install -y -q \
|
||||||
|
cmake \
|
||||||
|
git \
|
||||||
|
hwdata \
|
||||||
|
libglib2.0-0 \
|
||||||
|
libnuma1 \
|
||||||
|
libpixman-1-0 \
|
||||||
|
linux-tools-virtual \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
RUN update-alternatives --install /usr/local/bin/usbip usbip `ls /usr/lib/linux-tools/*/usbip | tail -n1` 20
|
||||||
|
|
||||||
|
# QEMU
|
||||||
|
ENV QEMU_REL=esp-develop-20220919
|
||||||
|
ENV QEMU_SHA256=f6565d3f0d1e463a63a7f81aec94cce62df662bd42fc7606de4b4418ed55f870
|
||||||
|
ENV QEMU_DIST=qemu-${QEMU_REL}.tar.bz2
|
||||||
|
ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/${QEMU_REL}/${QEMU_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}
|
||||||
|
|
||||||
|
ENV PATH=/opt/qemu/bin:${PATH}
|
||||||
|
|
||||||
|
RUN groupadd --gid $USER_GID $CONTAINER_USER \
|
||||||
|
&& adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
|
||||||
|
&& usermod -a -G dialout $CONTAINER_USER
|
||||||
|
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"]
|
47
firmware/.devcontainer/devcontainer.json
Normal file
47
firmware/.devcontainer/devcontainer.json
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
|
||||||
|
// https://github.com/microsoft/vscode-dev-containers/tree/v0.183.0/containers/ubuntu
|
||||||
|
{
|
||||||
|
"name": "ESP-IDF QEMU",
|
||||||
|
"build": {
|
||||||
|
"dockerfile": "Dockerfile"
|
||||||
|
},
|
||||||
|
// Add the IDs of extensions you want installed when the container is created
|
||||||
|
"workspaceMount": "source=${localWorkspaceFolder},target=${localWorkspaceFolder},type=bind",
|
||||||
|
/* the path of workspace folder to be opened after container is running
|
||||||
|
*/
|
||||||
|
"workspaceFolder": "${localWorkspaceFolder}",
|
||||||
|
"mounts": [
|
||||||
|
"source=extensionCache,target=/root/.vscode-server/extensions,type=volume"
|
||||||
|
],
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.espIdfPath": "/opt/esp/idf",
|
||||||
|
"idf.customExtraPaths": "",
|
||||||
|
"idf.pythonBinPath": "/opt/esp/python_env/idf5.1_py3.8_env/bin/python",
|
||||||
|
"idf.toolsPath": "/opt/esp",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"espressif.esp-idf-extension"
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"codespaces": {
|
||||||
|
"settings": {
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"idf.espIdfPath": "/opt/esp/idf",
|
||||||
|
"idf.customExtraPaths": "",
|
||||||
|
"idf.pythonBinPath": "/opt/esp/python_env/idf5.1_py3.8_env/bin/python",
|
||||||
|
"idf.toolsPath": "/opt/esp",
|
||||||
|
"idf.gitPath": "/usr/bin/git"
|
||||||
|
},
|
||||||
|
"extensions": [
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
"espressif.esp-idf-extension"
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runArgs": ["--privileged"]
|
||||||
|
}
|
3
firmware/.gitignore
vendored
Normal file
3
firmware/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
build/
|
||||||
|
sdkconfig
|
||||||
|
sdkconfig.old
|
26
firmware/.vscode/c_cpp_properties.json
vendored
Normal file
26
firmware/.vscode/c_cpp_properties.json
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "ESP-IDF",
|
||||||
|
"compilerPath": "",
|
||||||
|
"includePath": [
|
||||||
|
"${config:idf.espIdfPath}/components/**",
|
||||||
|
"${config:idf.espIdfPathWin}/components/**",
|
||||||
|
"${config:idf.espAdfPath}/components/**",
|
||||||
|
"${config:idf.espAdfPathWin}/components/**",
|
||||||
|
"${workspaceFolder}/**"
|
||||||
|
],
|
||||||
|
"browse": {
|
||||||
|
"path": [
|
||||||
|
"${config:idf.espIdfPath}/components",
|
||||||
|
"${config:idf.espIdfPathWin}/components",
|
||||||
|
"${config:idf.espAdfPath}/components/**",
|
||||||
|
"${config:idf.espAdfPathWin}/components/**",
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"limitSymbolsToIncludedHeaders": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
10
firmware/.vscode/launch.json
vendored
Normal file
10
firmware/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "espidf",
|
||||||
|
"name": "Launch",
|
||||||
|
"request": "launch"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
firmware/.vscode/settings.json
vendored
Normal file
3
firmware/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"C_Cpp.intelliSenseEngine": "default"
|
||||||
|
}
|
300
firmware/.vscode/tasks.json
vendored
Normal file
300
firmware/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "Build - Build project",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py build",
|
||||||
|
"windows": {
|
||||||
|
"command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py build",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": [
|
||||||
|
"relative",
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Set ESP-IDF Target",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${command:espIdf.setTarget}",
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^(.*):(//d+):(//d+)://s+(warning|error)://s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Clean - Clean the project",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py fullclean",
|
||||||
|
"windows": {
|
||||||
|
"command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py fullclean",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": [
|
||||||
|
"relative",
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Flash - Flash the device",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} -b ${config:idf.flashBaudRate} flash",
|
||||||
|
"windows": {
|
||||||
|
"command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py flash -p ${config:idf.portWin} -b ${config:idf.flashBaudRate}",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": [
|
||||||
|
"relative",
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Monitor: Start the monitor",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} monitor",
|
||||||
|
"windows": {
|
||||||
|
"command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py -p ${config:idf.portWin} monitor",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": [
|
||||||
|
"relative",
|
||||||
|
"${workspaceFolder}"
|
||||||
|
],
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dependsOn": "Flash - Flash the device"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "OpenOCD: Start openOCD",
|
||||||
|
"type": "shell",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "never",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "new"
|
||||||
|
},
|
||||||
|
"command": "openocd -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}",
|
||||||
|
"windows": {
|
||||||
|
"command": "openocd.exe -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": {
|
||||||
|
"owner": "cpp",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "adapter",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "${config:idf.pythonBinPath}",
|
||||||
|
"isBackground": true,
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH}:${config:idf.customExtraPaths}",
|
||||||
|
"PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": {
|
||||||
|
"background": {
|
||||||
|
"beginsPattern": "\bDEBUG_ADAPTER_STARTED\b",
|
||||||
|
"endsPattern": "DEBUG_ADAPTER_READY2CONNECT",
|
||||||
|
"activeOnStart": true
|
||||||
|
},
|
||||||
|
"pattern": {
|
||||||
|
"regexp": "(\\d+)-(\\d+)-(\\d+)\\s(\\d+):(\\d+):(\\d+),(\\d+)\\s-(.+)\\s(ERROR)",
|
||||||
|
"file": 8,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 9
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [
|
||||||
|
"${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter_main.py",
|
||||||
|
"-e",
|
||||||
|
"${workspaceFolder}/build/${command:espIdf.getProjectName}.elf",
|
||||||
|
"-s",
|
||||||
|
"${command:espIdf.getOpenOcdScriptValue}",
|
||||||
|
"-ip",
|
||||||
|
"localhost",
|
||||||
|
"-dn",
|
||||||
|
"${config:idf.adapterTargetName}",
|
||||||
|
"-om",
|
||||||
|
"connect_to_instance"
|
||||||
|
],
|
||||||
|
"windows": {
|
||||||
|
"command": "${config:idf.pythonBinPathWin}",
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"PATH": "${env:PATH};${config:idf.customExtraPaths}",
|
||||||
|
"PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
6
firmware/CMakeLists.txt
Normal file
6
firmware/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# 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.5)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(fablight)
|
15
firmware/dependencies.lock
Normal file
15
firmware/dependencies.lock
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
component_hash: 1f8cc130ebd557fde64c02fe5fad0cb36d69947498c540ace6f425e1083d7773
|
||||||
|
source:
|
||||||
|
service_url: https://api.components.espressif.com/
|
||||||
|
type: service
|
||||||
|
version: 2.4.1
|
||||||
|
idf:
|
||||||
|
component_hash: null
|
||||||
|
source:
|
||||||
|
type: idf
|
||||||
|
version: 5.0.1
|
||||||
|
manifest_hash: 866a017870fcec1240064adf450d5b56fe14dc761ed21f016308418230712755
|
||||||
|
target: esp32
|
||||||
|
version: 1.0.0
|
2
firmware/main/CMakeLists.txt
Normal file
2
firmware/main/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
605
firmware/main/FabLight.c
Normal file
605
firmware/main/FabLight.c
Normal file
@ -0,0 +1,605 @@
|
|||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
|
#include <mqtt_client.h>
|
||||||
|
#include <esp_crt_bundle.h>
|
||||||
|
#include <led_strip.h>
|
||||||
|
#include <esp_timer.h>
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
|
#include "LED_Segments.h"
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||||
|
#define TOPIC_BUFFER_SIZE 50
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
LED_Segments segments[CONFIG_LED_SEGMENT_COUNT];
|
||||||
|
|
||||||
|
esp_mqtt_client_handle_t mqtt_client;
|
||||||
|
led_strip_handle_t led_internal;
|
||||||
|
led_strip_handle_t led_segments;
|
||||||
|
|
||||||
|
uint64_t last_time_blink = 0;
|
||||||
|
uint64_t last_time_pulse = 0;
|
||||||
|
uint64_t last_time_walk_fill = 0;
|
||||||
|
int refresh_pulse = CONFIG_PATTERN_TIME / 2 / 255;
|
||||||
|
int refresh_blink = CONFIG_PATTERN_TIME / CONFIG_BLINK_COUNT;
|
||||||
|
int refresh_walk_fill = CONFIG_PATTERN_TIME / CONFIG_SEGMENT_COUNT;
|
||||||
|
int value_blink = 0;
|
||||||
|
int value_pulse = 0;
|
||||||
|
int value_walk_fill = 0;
|
||||||
|
int direction_pulse = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Function prototypes
|
||||||
|
esp_err_t init_mqtt(void);
|
||||||
|
|
||||||
|
esp_err_t handle_mqtt_cmd(char *topic, uint8_t *payload, int length);
|
||||||
|
|
||||||
|
// helper functions
|
||||||
|
uint8_t limit(uint8_t value) {
|
||||||
|
return (LIMIT * value) / 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
|
||||||
|
static void network_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) {
|
||||||
|
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, LIMIT, 0));
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(debug_led));
|
||||||
|
esp_wifi_connect();
|
||||||
|
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, LIMIT, 0));
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(debug_led));
|
||||||
|
esp_wifi_connect();
|
||||||
|
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
|
||||||
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
|
||||||
|
ESP_LOGI("network", "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
|
||||||
|
ESP_ERROR_CHECK(init_mqtt());
|
||||||
|
ESP_LOGI("main", "init_mqtt done");
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, LIMIT, 0));
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(debug_led));
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, 0, 0, 0));
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(debug_led));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Event handler registered to receive MQTT events
|
||||||
|
*
|
||||||
|
* This function is called by the MQTT client event loop.
|
||||||
|
*
|
||||||
|
* @param handler_args user data registered to the event.
|
||||||
|
* @param base Event base for the handler(always MQTT Base in this example).
|
||||||
|
* @param event_id The id for the received event.
|
||||||
|
* @param event_data The data for the event, esp_mqtt_event_handle_t.
|
||||||
|
*/
|
||||||
|
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
|
||||||
|
ESP_LOGD("mqtt", "Event dispatched from event loop base=%s, event_id=%" PRIu32, base, event_id);
|
||||||
|
esp_mqtt_event_handle_t event = event_data;
|
||||||
|
esp_mqtt_client_handle_t client = event->client;
|
||||||
|
int msg_id;
|
||||||
|
switch ((esp_mqtt_event_id_t) event_id) {
|
||||||
|
case MQTT_EVENT_CONNECTED:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_CONNECTED");
|
||||||
|
|
||||||
|
char id[6] = "00000";
|
||||||
|
sprintf(id, "%05d", CONFIG_FABLIGHT_ID);
|
||||||
|
|
||||||
|
// Register Reader
|
||||||
|
msg_id = esp_mqtt_client_publish(client, "fablight", id, 0, 0, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent publish successful, msg_id=%d", msg_id);
|
||||||
|
|
||||||
|
char topic_recv[TOPIC_BUFFER_SIZE];
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/static", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/pulse", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/blink", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/walk", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/+/fill", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/buzzer", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/sync", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/sync");
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
snprintf(topic_recv, TOPIC_BUFFER_SIZE, "fablight/%05d/clear", CONFIG_FABLIGHT_ID);
|
||||||
|
msg_id = esp_mqtt_client_subscribe(client, topic_recv, 0);
|
||||||
|
ESP_LOGI("mqtt", "sent subscribe successful, msg_id=%d", msg_id);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_DISCONNECTED:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_DISCONNECTED");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MQTT_EVENT_SUBSCRIBED:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_UNSUBSCRIBED:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_PUBLISHED:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_DATA:
|
||||||
|
ESP_LOGI("mqtt", "MQTT_EVENT_DATA");
|
||||||
|
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
|
||||||
|
printf("DATA=%.*s\r\n", event->data_len, event->data);
|
||||||
|
|
||||||
|
char* topic = strndup(event->topic, event->topic_len);
|
||||||
|
|
||||||
|
esp_err_t ret = handle_mqtt_cmd(topic, (uint8_t *) event->data, event->data_len);
|
||||||
|
|
||||||
|
free(topic);
|
||||||
|
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
ESP_LOGE("mqtt", "Error handling MQTT command: %s", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MQTT_EVENT_ERROR:
|
||||||
|
ESP_LOGE("mqtt", "MQTT_EVENT_ERROR");
|
||||||
|
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
|
||||||
|
ESP_LOGE("mqtt", "Last error code reported from esp-tls: 0x%x",
|
||||||
|
event->error_handle->esp_tls_last_esp_err);
|
||||||
|
ESP_LOGE("mqtt", "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err);
|
||||||
|
ESP_LOGE("mqtt", "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno,
|
||||||
|
strerror(event->error_handle->esp_transport_sock_errno));
|
||||||
|
} else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) {
|
||||||
|
ESP_LOGE("mqtt", "Connection refused error: 0x%x", event->error_handle->connect_return_code);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW("mqtt", "Unknown error type: 0x%x", event->error_handle->error_type);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGI("mqtt", "Other event id:%d", event->event_id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialization Functions
|
||||||
|
|
||||||
|
esp_err_t init_led_strip() {
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t debug_led_config = {
|
||||||
|
.strip_gpio_num = CONFIG_PIN_DEBUG_LED, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = 1, // The number of LEDs in the strip,
|
||||||
|
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to invert the output signal
|
||||||
|
};
|
||||||
|
|
||||||
|
// LED strip backend configuration: RMT
|
||||||
|
led_strip_rmt_config_t debug_led_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
|
||||||
|
.flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&debug_led_config, &debug_led_rmt_config, &debug_led));
|
||||||
|
ESP_LOGI("led", "Created LED strip object with RMT backend for debug LED");
|
||||||
|
|
||||||
|
// LED strip general initialization, according to your led board design
|
||||||
|
led_strip_config_t strip_config = {
|
||||||
|
.strip_gpio_num = CONFIG_PIN_SEGMENT_DATA, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = CONFIG_SEGMENT_COUNT * CONFIG_SEGMENT_SIZE, // The number of LEDs in the strip,
|
||||||
|
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to 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
|
||||||
|
.flags.with_dma = true, // DMA feature is available on ESP target like ESP32-S3
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
|
||||||
|
ESP_LOGI("led", "Created LED strip object with RMT backend for Segment LEDs");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t init_gpio(void) {
|
||||||
|
gpio_config_t io_conf = {};
|
||||||
|
//disable interrupt
|
||||||
|
io_conf.intr_type = GPIO_INTR_DISABLE;
|
||||||
|
//set as output mode
|
||||||
|
io_conf.mode = GPIO_MODE_OUTPUT;
|
||||||
|
//bit mask of the pins that you want to set,e.g.GPIO18/19
|
||||||
|
io_conf.pin_bit_mask = (1ULL << CONFIG_PIN_BUZZER);
|
||||||
|
//disable pull-down mode
|
||||||
|
io_conf.pull_down_en = 0;
|
||||||
|
//disable pull-up mode
|
||||||
|
io_conf.pull_up_en = 0;
|
||||||
|
//configure GPIO with the given settings
|
||||||
|
esp_err_t ret = gpio_config(&io_conf);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t init_eventloop() {
|
||||||
|
esp_err_t ret = esp_event_loop_create_default();
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t init_wifi() {
|
||||||
|
esp_err_t ret;
|
||||||
|
ret = esp_netif_init();
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||||
|
ret = esp_wifi_init(&cfg);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
ret = esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &network_event_handler, NULL, NULL);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
ret = esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &network_event_handler, NULL, NULL);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
|
||||||
|
// Initialize default station as network interface instance (esp-netif)
|
||||||
|
esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
|
||||||
|
assert(sta_netif);
|
||||||
|
|
||||||
|
// Initialize and start WiFi
|
||||||
|
wifi_config_t wifi_config = {
|
||||||
|
.sta = {
|
||||||
|
.ssid = CONFIG_WIFI_SSID,
|
||||||
|
.password = CONFIG_WIFI_PASSWORD,
|
||||||
|
.scan_method = CONFIG_WIFI_FAST_SCAN ? WIFI_FAST_SCAN : WIFI_ALL_CHANNEL_SCAN,
|
||||||
|
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
|
||||||
|
.threshold.rssi = -127,
|
||||||
|
.threshold.authmode = WIFI_AUTH_WPA2_PSK,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ret = esp_wifi_set_mode(WIFI_MODE_STA);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
ret = esp_wifi_set_config(WIFI_IF_STA, &wifi_config);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
ret = esp_wifi_start();
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t init_nvs() {
|
||||||
|
// Initialize NVS
|
||||||
|
esp_err_t ret = nvs_flash_init();
|
||||||
|
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||||
|
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||||
|
ret = nvs_flash_init();
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t init_mqtt() {
|
||||||
|
esp_err_t ret;
|
||||||
|
const esp_mqtt_client_config_t mqtt_cfg = {
|
||||||
|
.broker = {
|
||||||
|
.address.uri = CONFIG_BROKER_URI,
|
||||||
|
.verification.crt_bundle_attach = esp_crt_bundle_attach
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
|
||||||
|
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
|
||||||
|
ret = esp_mqtt_client_register_event(client, MQTT_EVENT_ANY, mqtt_event_handler, NULL);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
ret = esp_mqtt_client_start(client);
|
||||||
|
ESP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
mqtt_client = client;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buisness Logic
|
||||||
|
|
||||||
|
esp_err_t handle_mqtt_cmd(char *topic, uint8_t *payload, int length) {
|
||||||
|
int topic_len = (int) strlen(topic);
|
||||||
|
int mode_static_len = (int) strlen("/static");
|
||||||
|
int mode_pulse_len = (int) strlen("/pulse");
|
||||||
|
int mode_blink_len = (int) strlen("/blink");
|
||||||
|
int mode_fill_len = (int) strlen("/fill");
|
||||||
|
int mode_walk_len = (int) strlen("/walk");
|
||||||
|
int buzzer_len = (int) strlen("/buzzer");
|
||||||
|
int sync_len = (int) strlen("/sync");
|
||||||
|
int clear_len = (int) strlen("/clear");
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - mode_static_len, "/static")) {
|
||||||
|
if (length != 3) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str_id[6] = {0};
|
||||||
|
strncpy(str_id, topic + topic_len - mode_static_len - 5, 5);
|
||||||
|
int id = strtol(str_id, NULL, 10);
|
||||||
|
|
||||||
|
if (id >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_SIZE; i++) {
|
||||||
|
ESP_ERROR_CHECK(
|
||||||
|
led_strip_set_pixel(led_strip, id * CONFIG_SEGMENT_SIZE + i, limit(payload[0]), limit(payload[1]),
|
||||||
|
limit(payload[2])));
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
|
||||||
|
led_color[id][0] = MODE_STATIC;
|
||||||
|
led_color[id][1] = limit(payload[0]);
|
||||||
|
led_color[id][2] = limit(payload[1]);
|
||||||
|
led_color[id][3] = limit(payload[2]);
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set segment %d to static color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - mode_pulse_len, "/pulse")) {
|
||||||
|
if (length != 3) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str_id[6] = {0};
|
||||||
|
strncpy(str_id, topic + topic_len - mode_pulse_len - 5, 5);
|
||||||
|
int id = strtol(str_id, NULL, 10);
|
||||||
|
|
||||||
|
if (id >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_color[id][0] = MODE_PULSE;
|
||||||
|
led_color[id][1] = limit(payload[0]);
|
||||||
|
led_color[id][2] = limit(payload[1]);
|
||||||
|
led_color[id][3] = limit(payload[2]);
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set segment %d to pulse color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - mode_blink_len, "/blink")) {
|
||||||
|
if (length != 3) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str_id[6] = {0};
|
||||||
|
strncpy(str_id, topic + topic_len - mode_blink_len - 5, 5);
|
||||||
|
int id = strtol(str_id, NULL, 10);
|
||||||
|
|
||||||
|
if (id >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_color[id][0] = MODE_BLINK;
|
||||||
|
led_color[id][1] = limit(payload[0]);
|
||||||
|
led_color[id][2] = limit(payload[1]);
|
||||||
|
led_color[id][3] = limit(payload[2]);
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set segment %d to blink color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - mode_fill_len, "/fill")) {
|
||||||
|
if (length != 3) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str_id[6] = {0};
|
||||||
|
strncpy(str_id, topic + topic_len - mode_fill_len - 5, 5);
|
||||||
|
int id = strtol(str_id, NULL, 10);
|
||||||
|
|
||||||
|
if (id >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_color[id][0] = MODE_FILL;
|
||||||
|
led_color[id][1] = limit(payload[0]);
|
||||||
|
led_color[id][2] = limit(payload[1]);
|
||||||
|
led_color[id][3] = limit(payload[2]);
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set segment %d to fill color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - mode_walk_len, "/walk")) {
|
||||||
|
if (length != 3) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char str_id[6] = {0};
|
||||||
|
strncpy(str_id, topic + topic_len - mode_walk_len - 5, 5);
|
||||||
|
int id = strtol(str_id, NULL, 10);
|
||||||
|
|
||||||
|
if (id >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
led_color[id][0] = MODE_WALK;
|
||||||
|
led_color[id][1] = limit(payload[0]);
|
||||||
|
led_color[id][2] = limit(payload[1]);
|
||||||
|
led_color[id][3] = limit(payload[2]);
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set segment %d to walk color %d %d %d", id, led_color[id][1], led_color[id][2], led_color[id][3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - buzzer_len, "/buzzer")) {
|
||||||
|
if (length != 1) {
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload[0] == 0) {
|
||||||
|
gpio_set_level(CONFIG_PIN_BUZZER, 0);
|
||||||
|
} else {
|
||||||
|
gpio_set_level(CONFIG_PIN_BUZZER, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "set buzzer to %d", payload[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - sync_len, "/sync")) {
|
||||||
|
uint64_t time = esp_timer_get_time();
|
||||||
|
last_time_blink = time;
|
||||||
|
last_time_pulse = time;
|
||||||
|
last_time_walk_fill = time;
|
||||||
|
|
||||||
|
value_blink = 0;
|
||||||
|
value_pulse = 0;
|
||||||
|
value_walk_fill = 0;
|
||||||
|
direction_pulse = 0;
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "sync");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(topic + topic_len - clear_len, "/clear")) {
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(led_strip_clear(led_strip));
|
||||||
|
// ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) {
|
||||||
|
led_color[i][0] = MODE_OFF;
|
||||||
|
led_color[i][1] = 0;
|
||||||
|
led_color[i][2] = 0;
|
||||||
|
led_color[i][3] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI("mqtt_handler", "clear");
|
||||||
|
}
|
||||||
|
ESP_LOGI("mqtt_handler", "topic: %s", topic);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_Noreturn void app_main(void) {
|
||||||
|
init_led_strip();
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(debug_led, 0, LIMIT, 0, 0));
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(debug_led));
|
||||||
|
|
||||||
|
init_gpio();
|
||||||
|
init_eventloop();
|
||||||
|
init_nvs();
|
||||||
|
init_wifi();
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (esp_timer_get_time() > last_time_blink + refresh_blink) {
|
||||||
|
last_time_blink = esp_timer_get_time();
|
||||||
|
value_blink = value_blink == 0 ? 1 : 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) {
|
||||||
|
if (led_color[i][0] == MODE_BLINK) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip, i * CONFIG_SEGMENT_SIZE + e, led_color[i][1] * value_blink, led_color[i][2] * value_blink,
|
||||||
|
led_color[i][3] * value_blink));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// PULSE
|
||||||
|
if (esp_timer_get_time() > last_time_pulse + refresh_pulse) {
|
||||||
|
last_time_pulse = esp_timer_get_time();
|
||||||
|
|
||||||
|
if (direction_pulse == 0) {
|
||||||
|
value_pulse--;
|
||||||
|
if (value_pulse <= 0) {
|
||||||
|
direction_pulse = 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value_pulse++;
|
||||||
|
if (value_pulse >= 255) {
|
||||||
|
direction_pulse = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) {
|
||||||
|
if (led_color[i][0] == MODE_PULSE) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip,
|
||||||
|
i * CONFIG_SEGMENT_SIZE + e,
|
||||||
|
(value_pulse * led_color[i][1]) / 255,
|
||||||
|
(value_pulse * led_color[i][2]) / 255,
|
||||||
|
(value_pulse * led_color[i][3]) / 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
}
|
||||||
|
|
||||||
|
// WALK or FILL
|
||||||
|
if (esp_timer_get_time() > last_time_walk_fill + refresh_walk_fill) {
|
||||||
|
last_time_walk_fill = esp_timer_get_time();
|
||||||
|
|
||||||
|
if (value_walk_fill >= CONFIG_SEGMENT_COUNT) {
|
||||||
|
value_walk_fill = 0;
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) {
|
||||||
|
if (led_color[i][0] == MODE_FILL) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip,
|
||||||
|
i * CONFIG_SEGMENT_SIZE + e,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value_walk_fill++;
|
||||||
|
if (value_walk_fill == CONFIG_SEGMENT_COUNT) {
|
||||||
|
for (int i = 0; i < CONFIG_SEGMENT_COUNT; i++) {
|
||||||
|
if (led_color[i][0] == MODE_FILL) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip,
|
||||||
|
i * CONFIG_SEGMENT_SIZE + e,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (led_color[value_walk_fill][0] == MODE_FILL || led_color[value_walk_fill][0] == MODE_WALK) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip,
|
||||||
|
value_walk_fill * CONFIG_SEGMENT_SIZE + e,
|
||||||
|
led_color[value_walk_fill][1],
|
||||||
|
led_color[value_walk_fill][2],
|
||||||
|
led_color[value_walk_fill][3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value_walk_fill > 0 && led_color[value_walk_fill - 1][0] == MODE_WALK) {
|
||||||
|
for (int e = 0; e < CONFIG_SEGMENT_SIZE; e++) {
|
||||||
|
ESP_ERROR_CHECK(led_strip_set_pixel(led_strip,
|
||||||
|
value_walk_fill - 1 * CONFIG_SEGMENT_SIZE + e,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_ERROR_CHECK(led_strip_refresh(led_strip));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
187
firmware/main/Kconfig.projbuild
Normal file
187
firmware/main/Kconfig.projbuild
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
menu "FabLight Configuration"
|
||||||
|
######### WLAN ###########
|
||||||
|
config WIFI_SSID
|
||||||
|
string "WiFi SSID"
|
||||||
|
default "myssid"
|
||||||
|
help
|
||||||
|
SSID (network name) for the example to connect to.
|
||||||
|
|
||||||
|
config WIFI_PASSWORD
|
||||||
|
string "WiFi Password"
|
||||||
|
default "mypassword"
|
||||||
|
help
|
||||||
|
WiFi password (WPA or WPA2) for the example to use.
|
||||||
|
|
||||||
|
choice WIFI_SCAN_METHOD
|
||||||
|
prompt "scan method"
|
||||||
|
default WIFI_FAST_SCAN
|
||||||
|
help
|
||||||
|
scan method for the esp32 to use
|
||||||
|
|
||||||
|
config WIFI_FAST_SCAN
|
||||||
|
bool "fast"
|
||||||
|
config WIFI_ALL_CHANNEL_SCAN
|
||||||
|
bool "all"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
######### MQTT ###########
|
||||||
|
config MQTT_URI
|
||||||
|
string "MQTT Broker URL"
|
||||||
|
default "mqtts://mqtt.eclipseprojects.io:8883"
|
||||||
|
help
|
||||||
|
URL of an mqtt broker which this example connects to.
|
||||||
|
|
||||||
|
config MQTT_CERTIFICATE_OVERRIDE
|
||||||
|
string "MQTT Broker certificate override"
|
||||||
|
default ""
|
||||||
|
help
|
||||||
|
Please leave empty if broker certificate included from a textfile; otherwise fill in a base64 part of PEM
|
||||||
|
format certificate
|
||||||
|
|
||||||
|
config MQTT_CERTIFICATE_OVERRIDDEN
|
||||||
|
bool
|
||||||
|
default y if MQTT_CERTIFICATE_OVERRIDE != ""
|
||||||
|
|
||||||
|
config MQTT_USER
|
||||||
|
string "MQTT User"
|
||||||
|
default "myuser"
|
||||||
|
help
|
||||||
|
Please leave empty if no Username for MQTT is required.
|
||||||
|
|
||||||
|
config MQTT_PASSWORD
|
||||||
|
string "MQTT Password"
|
||||||
|
default "mypassword"
|
||||||
|
help
|
||||||
|
Please leave empty if no Password for MQTT is required.
|
||||||
|
|
||||||
|
config MQTT_USE_USERNAME
|
||||||
|
bool
|
||||||
|
default y if MQTT_USER != ""
|
||||||
|
|
||||||
|
######### FABLIGHT ###########
|
||||||
|
config FABLIGHT_ID
|
||||||
|
int "FabLight ID"
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Numerical ID of the FabLight device
|
||||||
|
|
||||||
|
config FABLIGHT_KEEP_STATE
|
||||||
|
bool
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Keep state of LED segments after powerloss
|
||||||
|
|
||||||
|
config FABLIGHT_LED_TIMEOUT
|
||||||
|
int "FabLight LED Timeout"
|
||||||
|
default 120
|
||||||
|
help
|
||||||
|
Time in seconds, when FabLight clears LEDs on connection loss
|
||||||
|
|
||||||
|
######### FABLIGHT LEDS ###########
|
||||||
|
config LED_SEGMENT_COUNT
|
||||||
|
int "Segment count"
|
||||||
|
default 3
|
||||||
|
help
|
||||||
|
Number of different addressable segments
|
||||||
|
|
||||||
|
config LED_SEGMENT_SIZE
|
||||||
|
int "Segment size"
|
||||||
|
default 2
|
||||||
|
help
|
||||||
|
Number of LEDs per segment
|
||||||
|
|
||||||
|
config LED_PATTERN_TIME
|
||||||
|
int "Pattern time"
|
||||||
|
default 3000
|
||||||
|
help
|
||||||
|
Timebase for the patterns in milliseconds
|
||||||
|
|
||||||
|
config LED_BLINK_COUNT
|
||||||
|
int "Blink count"
|
||||||
|
default 6
|
||||||
|
help
|
||||||
|
Number of blinks per pattern
|
||||||
|
|
||||||
|
config LED_LIMIT
|
||||||
|
int "Brightness limt"
|
||||||
|
default 0xAA
|
||||||
|
help
|
||||||
|
Brightness limit to prevent overheating of enclosure
|
||||||
|
|
||||||
|
######### PINOUT ###########
|
||||||
|
config PIN_SEGMENT_DATA_0
|
||||||
|
int "Segment data pin 0"
|
||||||
|
default 10
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 0 data pin
|
||||||
|
config PIN_SEGMENT_DATA_1
|
||||||
|
int "Segment data pin 1"
|
||||||
|
default 11
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 1 data pin
|
||||||
|
config PIN_SEGMENT_DATA_2
|
||||||
|
int "Segment data pin 2"
|
||||||
|
default 12
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 2 data pin
|
||||||
|
config PIN_SEGMENT_DATA_3
|
||||||
|
int "Segment data pin 3"
|
||||||
|
default 13
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 3 data pin
|
||||||
|
config PIN_SEGMENT_DATA_4
|
||||||
|
int "Segment data pin 4"
|
||||||
|
default 14
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 4 data pin
|
||||||
|
config PIN_SEGMENT_DATA_5
|
||||||
|
int "Segment data pin 5"
|
||||||
|
default 21
|
||||||
|
help
|
||||||
|
GPIO pin number for the segment 5 data pin
|
||||||
|
|
||||||
|
config PIN_EXTERNAL_0
|
||||||
|
int "IO external 0"
|
||||||
|
default 4
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 0
|
||||||
|
config PIN_EXTERNAL_1
|
||||||
|
int "IO external 1"
|
||||||
|
default 5
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 1
|
||||||
|
config PIN_EXTERNAL_2
|
||||||
|
int "IO external 2"
|
||||||
|
default 6
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 2
|
||||||
|
config PIN_EXTERNAL_3
|
||||||
|
int "IO external 3"
|
||||||
|
default 7
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 3
|
||||||
|
config PIN_EXTERNAL_4
|
||||||
|
int "IO external 4"
|
||||||
|
default 15
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 4
|
||||||
|
config PIN_EXTERNAL_5
|
||||||
|
int "IO external 5"
|
||||||
|
default 16
|
||||||
|
help
|
||||||
|
GPIO pin number for the external data pin 5
|
||||||
|
|
||||||
|
config PIN_BUZZER
|
||||||
|
int "Buzzer pin"
|
||||||
|
default 21
|
||||||
|
help
|
||||||
|
GPIO pin number for the buzzer
|
||||||
|
|
||||||
|
config PIN_INTERNAL_LED
|
||||||
|
int "Internal LED data pin"
|
||||||
|
default 48
|
||||||
|
help
|
||||||
|
GPIO pin number for the internal LED
|
||||||
|
|
||||||
|
|
||||||
|
endmenu
|
25
firmware/main/LED_Segments.h
Normal file
25
firmware/main/LED_Segments.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef LED_SEGMENT
|
||||||
|
#define LED_SEGMENT
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum LED_Effect
|
||||||
|
{
|
||||||
|
STATIC = 0,
|
||||||
|
BLINK = 1,
|
||||||
|
PULSE = 2,
|
||||||
|
WALK = 3,
|
||||||
|
FILL = 4,
|
||||||
|
ROTATE = 5,
|
||||||
|
RAINBOW = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct LED_Segments
|
||||||
|
{
|
||||||
|
uint8_t segment_id;
|
||||||
|
uint8_t color;
|
||||||
|
enum LED_Effect effect;
|
||||||
|
} LED_Segment;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
17
firmware/main/idf_component.yml
Normal file
17
firmware/main/idf_component.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^2.3.1"
|
||||||
|
## Required IDF version
|
||||||
|
idf:
|
||||||
|
version: ">=4.1.0"
|
||||||
|
# # Put list of dependencies here
|
||||||
|
# # For components maintained by Espressif:
|
||||||
|
# component: "~1.0.0"
|
||||||
|
# # For 3rd party components:
|
||||||
|
# username/component: ">=1.0.0,<2.0.0"
|
||||||
|
# username2/component2:
|
||||||
|
# version: "~1.0.0"
|
||||||
|
# # For transient dependencies `public` flag can be set.
|
||||||
|
# # `public` flag doesn't have an effect dependencies of the `main` component.
|
||||||
|
# # All dependencies of `main` are public by default.
|
||||||
|
# public: true
|
44
firmware/main/main.c
Normal file
44
firmware/main/main.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <sys/cdefs.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <esp_err.h>
|
||||||
|
#include <esp_wifi.h>
|
||||||
|
#include <esp_event.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
#include <nvs_flash.h>
|
||||||
|
#include <mqtt_client.h>
|
||||||
|
#include <esp_crt_bundle.h>
|
||||||
|
#include <led_strip.h>
|
||||||
|
#include <esp_timer.h>
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
|
#include "LED_Segments.h"
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
#define LED_STRIP_RMT_RES_HZ (10 * 1000 * 1000)
|
||||||
|
#define TOPIC_BUFFER_SIZE 50
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
LED_Segments segments[CONFIG_LED_SEGMENT_COUNT];
|
||||||
|
|
||||||
|
esp_mqtt_client_handle_t mqtt_client;
|
||||||
|
led_strip_handle_t led_internal;
|
||||||
|
led_strip_handle_t led_segments;
|
||||||
|
|
||||||
|
// LED Task
|
||||||
|
void vTaskLED( void * pvParameters )
|
||||||
|
{
|
||||||
|
for( ;; )
|
||||||
|
{
|
||||||
|
// Task code goes here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
TaskHandle_t xHandle_LED = NULL;
|
||||||
|
xTaskCreate( vTaskLED, "LED", 200, NULL, tskIDLE_PRIORITY, &xHandle_LED );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
1f8cc130ebd557fde64c02fe5fad0cb36d69947498c540ace6f425e1083d7773
|
@ -0,0 +1,32 @@
|
|||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
- Support configurable SPI mode to contorl leds
|
||||||
|
- recommend to enable 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,18 @@
|
|||||||
|
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||||
|
|
||||||
|
set(srcs "src/led_strip_api.c")
|
||||||
|
|
||||||
|
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 something that was added 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()
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "include" "interface"
|
||||||
|
PRIV_REQUIRES "driver")
|
202
firmware/managed_components/espressif__led_strip/LICENSE
Normal file
202
firmware/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.
|
88
firmware/managed_components/espressif__led_strip/README.md
Normal file
88
firmware/managed_components/espressif__led_strip/README.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
## Backend Controllers
|
||||||
|
|
||||||
|
### 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].
|
||||||
|
|
||||||
|
#### Allocate LED Strip Object with RMT Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
|
||||||
|
/* LED strip initialization with the GPIO and pixels number*/
|
||||||
|
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_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
|
||||||
|
};
|
||||||
|
|
||||||
|
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, // 10MHz
|
||||||
|
.flags.with_dma = false, // whether to enable the DMA feature
|
||||||
|
};
|
||||||
|
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 the RMT channel for you if there is more available.
|
||||||
|
|
||||||
|
### 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. However this backend is not as economical as the RMT one, because it will take up the whole **bus**, unlike the RMT just takes one **channel**. You **CANT** 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".
|
||||||
|
|
||||||
|
#### Allocate LED Strip Object with SPI Backend
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define BLINK_GPIO 0
|
||||||
|
|
||||||
|
led_strip_handle_t led_strip;
|
||||||
|
|
||||||
|
/* LED strip initialization with the GPIO and pixels number*/
|
||||||
|
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_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to invert the output signal (useful when your hardware has a level inverter)
|
||||||
|
};
|
||||||
|
|
||||||
|
led_strip_spi_config_t spi_config = {
|
||||||
|
.clk_src = SPI_CLK_SRC_DEFAULT, // different clock source can lead to different power consumption
|
||||||
|
.flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
};
|
||||||
|
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 buses are free to use in your project.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
* Which led_strip backend should I choose?
|
||||||
|
* It depends on your application requirement and target chip's ability.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A{Is RMT supported?}
|
||||||
|
A --> |No| B[SPI backend]
|
||||||
|
B --> C{Does the led strip has \n a larger number of LEDs?}
|
||||||
|
C --> |No| D[Don't have to enable the DMA of the backend]
|
||||||
|
C --> |Yes| E[Enable the DMA of the backend]
|
||||||
|
A --> |Yes| F{Does the led strip has \n a larger number of LEDs?}
|
||||||
|
F --> |Yes| G{Does RMT support DMA?}
|
||||||
|
G --> |Yes| E
|
||||||
|
G --> |No| B
|
||||||
|
F --> |No| H[RMT backend] --> D
|
||||||
|
```
|
||||||
|
|
||||||
|
[^1]: The RMT DMA feature is not available on all ESP chips. Please check the data sheet before using it.
|
@ -0,0 +1,8 @@
|
|||||||
|
# For more information about build system see
|
||||||
|
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||||
|
# 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.16)
|
||||||
|
|
||||||
|
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,5 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: '^2'
|
||||||
|
override_path: '../../../'
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 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_BLINK_GPIO 2
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_NUMBERS 24
|
||||||
|
// 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_BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_NUMBERS, // The number of LEDs in the strip,
|
||||||
|
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to 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
|
||||||
|
.flags.with_dma = false, // DMA feature is available on ESP target like ESP32-S3
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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_NUMBERS; 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,8 @@
|
|||||||
|
# For more information about build system see
|
||||||
|
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||||
|
# 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.16)
|
||||||
|
|
||||||
|
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,6 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
espressif/led_strip:
|
||||||
|
version: '^2.4'
|
||||||
|
override_path: '../../../'
|
||||||
|
idf: ">=5.1"
|
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2023 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_BLINK_GPIO 2
|
||||||
|
// Numbers of the LED in the strip
|
||||||
|
#define LED_STRIP_LED_NUMBERS 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_BLINK_GPIO, // The GPIO that connected to the LED strip's data line
|
||||||
|
.max_leds = LED_STRIP_LED_NUMBERS, // The number of LEDs in the strip,
|
||||||
|
.led_pixel_format = LED_PIXEL_FORMAT_GRB, // Pixel format of your LED strip
|
||||||
|
.led_model = LED_MODEL_WS2812, // LED strip model
|
||||||
|
.flags.invert_out = false, // whether to 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
|
||||||
|
.flags.with_dma = true, // Using DMA can improve performance and help drive more LEDs
|
||||||
|
.spi_bus = SPI2_HOST, // SPI bus ID
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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_NUMBERS; 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 @@
|
|||||||
|
dependencies:
|
||||||
|
idf:
|
||||||
|
version: '>=5.0'
|
||||||
|
description: Driver for Addressable LED Strip (WS2812, etc)
|
||||||
|
url: https://github.com/espressif/idf-extra-components/tree/master/led_strip
|
||||||
|
version: 2.4.1
|
@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 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 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,45 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "driver/rmt_types.h"
|
||||||
|
#include "led_strip_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. */
|
||||||
|
struct {
|
||||||
|
uint32_t with_dma: 1; /*!< Use DMA to transmit data */
|
||||||
|
} 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,46 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 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;
|
||||||
|
} 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,54 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip pixel format
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LED_PIXEL_FORMAT_GRB, /*!< Pixel format: GRB */
|
||||||
|
LED_PIXEL_FORMAT_GRBW, /*!< Pixel format: GRBW */
|
||||||
|
LED_PIXEL_FORMAT_INVALID /*!< Invalid pixel format */
|
||||||
|
} led_pixel_format_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_INVALID /*!< Invalid LED strip model */
|
||||||
|
} led_model_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED strip handle
|
||||||
|
*/
|
||||||
|
typedef struct led_strip_t *led_strip_handle_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LED Strip Configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
int strip_gpio_num; /*!< GPIO number that used by LED strip */
|
||||||
|
uint32_t max_leds; /*!< Maximum LEDs in a single strip */
|
||||||
|
led_pixel_format_t led_pixel_format; /*!< LED pixel format */
|
||||||
|
led_model_t led_model; /*!< LED model */
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t invert_out: 1; /*!< Invert output signal */
|
||||||
|
} 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,41 @@
|
|||||||
|
/*
|
||||||
|
* 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_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,164 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 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;
|
||||||
|
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");
|
||||||
|
uint32_t start = index * rmt_strip->bytes_per_pixel;
|
||||||
|
// In thr order of GRB, as LED strip like WS2812 sends out pixels in this order
|
||||||
|
rmt_strip->pixel_buf[start + 0] = green & 0xFF;
|
||||||
|
rmt_strip->pixel_buf[start + 1] = red & 0xFF;
|
||||||
|
rmt_strip->pixel_buf[start + 2] = blue & 0xFF;
|
||||||
|
if (rmt_strip->bytes_per_pixel > 3) {
|
||||||
|
rmt_strip->pixel_buf[start + 3] = 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);
|
||||||
|
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(rmt_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||||
|
uint8_t *buf_start = rmt_strip->pixel_buf + index * 4;
|
||||||
|
// SK6812 component order is GRBW
|
||||||
|
*buf_start = green & 0xFF;
|
||||||
|
*++buf_start = red & 0xFF;
|
||||||
|
*++buf_start = blue & 0xFF;
|
||||||
|
*++buf_start = white & 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");
|
||||||
|
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||||
|
uint8_t bytes_per_pixel = 3;
|
||||||
|
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||||
|
bytes_per_pixel = 4;
|
||||||
|
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||||
|
bytes_per_pixel = 3;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
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->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,146 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "led_strip_rmt_encoder.h"
|
||||||
|
|
||||||
|
static const char *TAG = "led_rmt_encoder";
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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 {
|
||||||
|
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");
|
||||||
|
|
||||||
|
uint32_t reset_ticks = config->resolution / 1000000 * 50 / 2; // reset code duration defaults to 50us
|
||||||
|
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,225 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2023 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 "hal/spi_hal.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;
|
||||||
|
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");
|
||||||
|
// LED_PIXEL_FORMAT_GRB takes 72bits(9bytes)
|
||||||
|
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||||
|
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||||
|
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||||
|
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||||
|
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||||
|
if (spi_strip->bytes_per_pixel > 3) {
|
||||||
|
__led_strip_spi_bit(0, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
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(spi_strip->bytes_per_pixel == 4, ESP_ERR_INVALID_ARG, TAG, "wrong LED pixel format, expected 4 bytes per pixel");
|
||||||
|
// LED_PIXEL_FORMAT_GRBW takes 96bits(12bytes)
|
||||||
|
uint32_t start = index * spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE;
|
||||||
|
// SK6812 component order is GRBW
|
||||||
|
memset(spi_strip->pixel_buf + start, 0, spi_strip->bytes_per_pixel * SPI_BYTES_PER_COLOR_BYTE);
|
||||||
|
__led_strip_spi_bit(green, &spi_strip->pixel_buf[start]);
|
||||||
|
__led_strip_spi_bit(red, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE]);
|
||||||
|
__led_strip_spi_bit(blue, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 2]);
|
||||||
|
__led_strip_spi_bit(white, &spi_strip->pixel_buf[start + SPI_BYTES_PER_COLOR_BYTE * 3]);
|
||||||
|
|
||||||
|
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");
|
||||||
|
ESP_GOTO_ON_FALSE(led_config->led_pixel_format < LED_PIXEL_FORMAT_INVALID, ESP_ERR_INVALID_ARG, err, TAG, "invalid led_pixel_format");
|
||||||
|
uint8_t bytes_per_pixel = 3;
|
||||||
|
if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRBW) {
|
||||||
|
bytes_per_pixel = 4;
|
||||||
|
} else if (led_config->led_pixel_format == LED_PIXEL_FORMAT_GRB) {
|
||||||
|
bytes_per_pixel = 3;
|
||||||
|
} else {
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
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 !CONFIG_IDF_TARGET_ESP32
|
||||||
|
//SPI_D_POL : This bit is used to set the idle polarity of MOSI. 1:high;0:low (ESP32 does not have this register, and its default polarity is low)
|
||||||
|
spi_dev_t *hw;
|
||||||
|
hw = SPI_LL_GET_HW(spi_strip->spi_host);
|
||||||
|
hw->ctrl.d_pol = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
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
|
||||||
|
ESP_GOTO_ON_FALSE(clock_resolution_khz == LED_STRIP_SPI_DEFAULT_RESOLUTION / 1000, ESP_ERR_NOT_SUPPORTED, err,
|
||||||
|
TAG, "unsupported clock resolution:%dKHz", clock_resolution_khz);
|
||||||
|
|
||||||
|
//send dummy data to ensure the initial level of MOSI is low
|
||||||
|
uint8_t dummy_data = 0x00;
|
||||||
|
spi_transaction_t tx_conf = {
|
||||||
|
.length = 8,
|
||||||
|
.tx_buffer = &dummy_data,
|
||||||
|
.rx_buffer = NULL,
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(spi_device_transmit(spi_strip->spi_device, &tx_conf), TAG, "dummy pixels by SPI failed");
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user