45 Commits

Author SHA1 Message Date
ceaa1d343a feat: 支持通过 UDP 接收网速数据并显示在界面上。 2023-05-15 21:06:09 +08:00
f1ab7aa361 feat: 支持通过 udp 获取灯条颜色校准信息。 2023-05-12 20:39:24 +08:00
7a0d5d7fcd feat: 支持从桌面端收取电脑音量和显示器亮度。 2023-05-11 21:56:33 +08:00
7c9c016aef fix: 快速旋转 EC11 时,有概率界面卡死或崩溃。 2023-05-10 21:31:18 +08:00
3bdd8d70c3 feat(gui): 自动隐藏配置面板。 2023-05-07 23:39:52 +08:00
04a36d14a1 fix: 状态图标动画停止后无法正确显示指定的图标。 2023-05-07 22:09:16 +08:00
ac8520f31f feat: 旋转旋钮支持按速度进行增益。 2023-05-07 21:40:11 +08:00
df476e4f9c docs: 更新 udp 数据格式文档。 2023-05-07 19:09:39 +08:00
6e95095db0 feat: 支持向桌面端发送音量和显示器亮度数据。 2023-05-06 20:08:45 +08:00
a3abff89c5 feat: 电脑音量和显示器亮度设置界面。 2023-05-03 23:55:54 +08:00
3f5ca686cf Merge pull request 'Feature: 支持 LVGL 作为嵌入式显示屏的底层。' (#7) from feature/lvgl into master
Reviewed-on: #7
2023-05-03 21:04:25 +08:00
ca9e25f40c chore: change bar value label font size. 2023-05-03 20:43:33 +08:00
27270c4f6a feat(gui): 灯条亮度调整界面。 close #6. 2023-05-03 18:10:21 +08:00
324a307fe3 feat: desktop connection status icon. 2023-05-03 00:05:04 +08:00
b7476428a2 feat: 联网状态指示图标。 2023-05-02 20:06:30 +08:00
7098bf4665 feat(lvgl): base support. 2023-05-02 14:20:37 +08:00
0f5e2a9664 feat: ping pong. 2023-04-30 23:23:25 +08:00
05660afc54 fix: udp 服务端崩溃后无法重启 2023-04-28 15:40:07 +08:00
101103de03 feat: 使用 UDP 接收颜色。 2023-04-28 00:26:41 +08:00
7bff8e2c17 feat: 通过 mDNS 发布服务节点。 2023-04-24 20:41:44 +08:00
2da0f224a1 Merge pull request 'feat: 更新到 ESP-IDF v5.0.1.' (#5) from build/esp-idf-5 into master
Reviewed-on: #5
2023-04-24 20:27:16 +08:00
f0a1dae657 feat: 更新到 ESP-IDF v5.0.1. 2023-04-24 17:47:37 +08:00
58d1039c78 chore(light): 调整氛围灯最暗阈值。 2023-04-23 22:30:43 +08:00
e947dc3ac1 Merge commit 'a54c554342176526ffc9a54f779cd552a5627530' 2023-04-19 22:16:10 +08:00
c8eb0817e8 feat: 使用校准的颜色来显示空闲灯光。 2023-04-19 20:24:34 +08:00
ac33f67d77 feat: 使用校准的颜色来初始化灯光;避免使用全黑的灯光。 2023-04-18 00:03:23 +08:00
c27418aaf1 feat: 记住颜色校准的值。 2023-04-17 21:27:09 +08:00
40dde8cc89 feat: 支持颜色校准 2023-04-17 20:49:10 +08:00
a54c554342 feat(apds9960): 功能恢复。 2023-04-02 19:29:12 +08:00
eaedd765ed feat(apds9960): 防中断导致假死。 2023-03-11 12:05:25 +08:00
895aa3058d feat(apds9960): 手势解析。 2023-03-11 10:46:24 +08:00
33e08edeed feat(apds9960): 手势原始数据读取 2023-03-08 23:02:48 +08:00
e426829aa6 feat: 测试中断功能(接近) 2023-03-08 14:19:35 +08:00
f6b7a398cd feat: 将环境光、距离传感器从 9930 升级到 APDS9960. #4. 2023-03-06 20:57:25 +08:00
93e8f2beda pref: 减少编码器输入的中断触发次数。 close #3. 2023-03-05 21:58:22 +08:00
d3adb8cd56 feat: 支持 IO 扩展芯片 PCA9555 与 EC11 编码器的集成。 issue #3. 2023-03-05 21:47:20 +08:00
66307c14a2 feat(pca9555): 编码器数据读取。 2023-03-04 17:40:26 +08:00
bce9548aba fix: 温度传感器扫描不到。 2023-03-04 16:24:35 +08:00
4b716751a2 fix: 温度传感器不识别。 2023-03-04 13:50:18 +08:00
58d0cc55ae chore: ignore vscode dir. 2023-02-26 23:44:51 +08:00
a869036d34 feat: 启动时检测在线的 I2C 设备,避免不在线时崩溃。 2023-02-26 23:43:17 +08:00
ded155d9b5 feat: 语音模块氛围灯控制及亮度调整动画。 2023-02-25 23:13:56 +08:00
13a00fbe1b feat: 支持语音模块。 2023-02-24 21:29:15 +08:00
0b1e0ef67c feat: 完善显示器、氛围灯亮度调整。#2. 2023-02-18 15:19:26 +08:00
fbbf31e4e3 style: 代码样式调整。 2023-02-07 23:49:43 +08:00
39 changed files with 5968 additions and 1108 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
build/ build/
sdkconfig sdkconfig
sdkconfig.old sdkconfig.old
.vscode/
managed_components/
.DS_Store

View File

@@ -1,30 +0,0 @@
{
"configurations": [
{
"name": "ESP-IDF",
"compilerPath": "/Users/ivan/.espressif/tools/riscv32-esp-elf/esp-2021r2-patch5-8.4.0/riscv32-esp-elf/bin/riscv32-esp-elf-gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"includePath": [
"${config:idf.espIdfPath}/components/**",
"${config:idf.espIdfPathWin}/components/**",
"${config:idf.espAdfPath}/components/**",
"${config:idf.espAdfPathWin}/components/**",
"${workspaceFolder}/**"
],
"browse": {
"path": [
"/Users/ivan/esp/esp-idf/components",
"${config:idf.espIdfPathWin}/components",
"/Users/ivan/esp/esp-idf/components/**",
"${config:idf.espAdfPathWin}/components/**",
"${workspaceFolder}"
],
"limitSymbolsToIncludedHeaders": false
},
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}

10
.vscode/launch.json vendored
View File

@@ -1,10 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "espidf",
"name": "Launch",
"request": "launch"
}
]
}

51
.vscode/settings.json vendored
View File

@@ -1,51 +0,0 @@
{
"C_Cpp.intelliSenseEngine": "Tag Parser",
"idf.adapterTargetName": "esp32c3",
"idf.customExtraPaths": "/Users/ivan/.espressif/tools/xtensa-esp32-elf/esp-2021r2-patch5-8.4.0/xtensa-esp32-elf/bin:/Users/ivan/.espressif/tools/xtensa-esp32s2-elf/esp-2021r2-patch5-8.4.0/xtensa-esp32s2-elf/bin:/Users/ivan/.espressif/tools/xtensa-esp32s3-elf/esp-2021r2-patch5-8.4.0/xtensa-esp32s3-elf/bin:/Users/ivan/.espressif/tools/riscv32-esp-elf/esp-2021r2-patch5-8.4.0/riscv32-esp-elf/bin:/Users/ivan/.espressif/tools/esp32ulp-elf/2.35_20220830/esp32ulp-elf/bin:/Users/ivan/.espressif/tools/cmake/3.23.1/CMake.app/Contents/bin:/Users/ivan/.espressif/tools/openocd-esp32/v0.11.0-esp32-20220706/openocd-esp32/bin:/Users/ivan/.espressif/tools/ninja/1.10.2",
"idf.customExtraVars": "{\"OPENOCD_SCRIPTS\":\"/Users/ivan/.espressif/tools/openocd-esp32/v0.11.0-esp32-20220706/openocd-esp32/share/openocd/scripts\"}",
"idf.espIdfPath": "/Users/ivan/esp/esp-idf",
"idf.openOcdConfigs": [
"board/esp32c3-builtin.cfg"
],
"idf.port": "/dev/cu.usbmodem149201",
"idf.pythonBinPath": "/Users/ivan/.espressif/python_env/idf4.4_py3.8_env/bin/python",
"idf.toolsPath": "/Users/ivan/.espressif",
"idf.gitPath": "/usr/bin/git",
"idf.flashType": "UART",
"files.associations": {
"*.ejs": "html",
"css": "postcss",
"scss": "postcss",
"*.cjson": "jsonc",
"*.wxss": "css",
"*.wxs": "javascript",
"*.inc": "c",
"*.tcc": "c",
"*.ipp": "c",
"cstring": "cpp",
"system_error": "c",
"chrono": "c",
"random": "c",
"limits": "c",
"array": "c",
"string": "c",
"string_view": "c",
"esp_bit_defs.h": "c",
"bitset": "c",
"initializer_list": "c",
"regex": "c",
"utility": "c",
"deque": "c",
"list": "c",
"unordered_map": "c",
"unordered_set": "c",
"vector": "c",
"freertos.h": "c",
"task.h": "c",
"led_strip.h": "c",
"esp_event.h": "c",
"string.h": "c",
"i2c.h": "c",
"esp_log.h": "c"
}
}

300
.vscode/tasks.json vendored
View File

@@ -1,300 +0,0 @@
{
"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"
}
}
}
}
]
}

View File

@@ -4,7 +4,5 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5) cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/led_strip)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(display-ambient-light-board) project(display-ambient-light-board)

Binary file not shown.

Binary file not shown.

21
dependencies.lock Normal file
View File

@@ -0,0 +1,21 @@
dependencies:
espressif/mdns:
component_hash: 3f3bf8ff67ec99dde4e935637af087ebc899b9fee396ffa1c9138d3775f1aca4
source:
service_url: https://api.components.espressif.com/
type: service
version: 1.0.9
idf:
component_hash: null
source:
type: idf
version: 5.0.1
lvgl/lvgl:
component_hash: 0f2006e7b800eee17b73ed4f92ffbaa76d61c02ba4779ec9efbcd5f453bb0102
source:
service_url: https://api.components.espressif.com/
type: service
version: 8.3.6~1
manifest_hash: 9a88337b9db92c26041847f5fd4efc58df1dcf0e9c6b8758da96aaac2e63c2a2
target: esp32c3
version: 1.0.0

36
docs/udp.md Normal file
View File

@@ -0,0 +1,36 @@
# 基于 UDP 的上、下位机通讯规则
## 报文格式
开始的一个字节是命令,剩余字节是数据。缓冲区长度设为 1024 字节,所以单个 UDP 报文的数据最多为 1023 字节。
## 命令
### 上位机在线 `1`
### 更新灯带颜色 `2`
| 数据 | 长度(字节) | 说明 |
| --- | --- | --- |
| 起始位置 | 2 | 0~65535 |
| 长度 | 2 | 0~65535 |
| 颜色 | 3 | RGB 顺序,$2^3 * 2^3 * 2^3 = 65535$ 真彩色 |
### 更新电脑显示器亮度 `3`
| 数据 | 长度(字节) | 说明 |
| --- | --- | --- |
| 显示器序号 | 1 | 0~255 |
| 亮度 | 1 | 0~255 |
### 更新电脑音量 `4`
| 数据 | 长度(字节) | 说明 |
| --- | --- | --- |
| 音量 | 1 | 0~255 |
### 更新灯带基本颜色校准 `5`
| 数据 | 长度(字节) | 说明 |
| --- | --- | --- |
| 颜色 | 3 | R、G、B 亮度上限 |

View File

@@ -1,2 +1,24 @@
idf_component_register(SRCS "ambient_light.c" "temperature.c" "embedded_display.c" "ui.c" "mqtt.c" "main.c" "wifi.c" "light.c" "mqtt.c" idf_component_register(
INCLUDE_DIRS ".") SRCS
"service_discovery.c"
"app_nvs.c"
"ch1116.c"
# "apds_9960.c"
"pca9555.c"
"i2c.c"
"asr_pro.c"
"ci_03t.c"
"ui_input.c"
# "ambient_light.c"
# "temperature.c"
#
"desktop.c"
"main.c"
"wifi.c"
"light.c"
"led_strip_encoder/led_strip_encoder.c"
"gui.c"
"net_gateway_monitor.c"
"app_icon_8.c"
INCLUDE_DIRS "."
)

View File

@@ -65,57 +65,57 @@ menu "MQTT Configuration"
endmenu endmenu
menu "Encoder Configuration" menu "Encoder Configuration"
config ENCODER_0_CLK_PIN config ENCODER_0_CLK_PORT_IO
int "encoder 0 click GPIO" int "encoder 0 clock IO"
range 1 32 range 0 7
default 1
help
Encoder 0 clock io on PCA9555 port 1
config ENCODER_0_DT_PORT_IO
int "encoder 0 data IO"
range 0 7
default 2 default 2
help
Encoder 0 clock pin
config ENCODER_0_DT_PIN
int "encoder 0 data GPIO"
range 1 32
default 3
help help
Encoder 0 clock data Encoder 0 clock data
config ENCODER_0_SW_PIN config ENCODER_0_SW_PORT_IO
int "encoder 0 switch GPIO" int "encoder 0 switch IO"
range 1 32 range 0 7
default 0
help
Encoder 0 switch io on PCA9555 port 1
config ENCODER_1_CLK_PORT_IO
int "encoder 1 clock IO"
range 0 7
default 4 default 4
help help
Encoder 0 switch pin Encoder 1 clock io on PCA9555 port 1
config ENCODER_1_DT_PORT_IO
config ENCODER_1_CLK_PIN int "encoder 1 data IO"
int "encoder 1 click GPIO" range 0 7
range 1 32
default 5 default 5
help
Encoder 1 clock pin
config ENCODER_1_DT_PIN
int "encoder 1 data GPIO"
range 1 32
default 6
help help
Encoder 1 clock data Encoder 1 clock data
config ENCODER_1_SW_PIN config ENCODER_1_SW_PORT_IO
int "encoder 1 switch GPIO" int "encoder 1 switch IO"
range 1 32 range 0 7
default 7 default 3
help help
Encoder 1 switch pin Encoder 1 switch io on PCA9555 port 1
endmenu endmenu
menu "I2C Configuration" menu "I2C Configuration"
config I2C_SCL config I2C_SCL
int "I2C SCL GPIO" int "I2C SCL GPIO"
range 1 32 range 0 32
default 8 default 5
help help
I2C SCL GPIO I2C SCL GPIO
config I2C_SDA config I2C_SDA
int "I2C SDA GPIO" int "I2C SDA GPIO"
range 1 32 range 0 32
default 10 default 4
help help
I2C SDA GPIO I2C SDA GPIO
config I2C_NUM config I2C_NUM
@@ -125,3 +125,33 @@ menu "I2C Configuration"
help help
I2C NUM I2C NUM
endmenu endmenu
menu "UART Configuration"
config UART_TX
int "UART TX GPIO"
range 0 32
default 21
help
UART TX GPIO
config UART_RX
int "UART RX GPIO"
range 0 32
default 20
help
UART RX GPIO
config UART_NUM
int "UART NUM"
range 0 1
default 0
help
UART NUM
endmenu
menu "ADPS 9960"
config APDS_9960_INT_GPIO
int "APDS 996O INT GPIO"
range 0 32
default 2
help
APDS 996O INT GPIO
endmenu

View File

@@ -3,17 +3,8 @@
#include "driver/i2c.h" #include "driver/i2c.h"
#include "embedded_display.c" #include "embedded_display.c"
#include "esp_log.h" #include "esp_log.h"
#include "i2c.c"
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#define I2C_MASTER_NUM CONFIG_I2C_NUM
#define APDS_9930_ADDRESS 0x39
#define APDS_9930_CMD_REPEATED 0x80 // 命令重复地址 #define APDS_9930_CMD_REPEATED 0x80 // 命令重复地址
#define APDS_9930_CMD_AUTO_INCREMENT 0x90 // 命令自动递增地址 #define APDS_9930_CMD_AUTO_INCREMENT 0x90 // 命令自动递增地址
@@ -24,7 +15,7 @@
#define APDS_9930_REG_PTIME 0x02 // Proximity ADC 时间 0xff #define APDS_9930_REG_PTIME 0x02 // Proximity ADC 时间 0xff
#define APDS_9930_REG_WTIME 0x03 // Wait 时间 0xff #define APDS_9930_REG_WTIME 0x03 // Wait 时间 0xff
#define APDS_9930_REG_PPULSE 0x0e // Proximity 脉冲计数 0x00 #define APDS_9930_REG_PPULSE 0x0e // Proximity 脉冲计数 0x00
#define APDS_9930_REG_CONTROL 0x0f // 增益控制 0x00 #define APDS_9930_REG_CONTROL 0x0f // 增益控制 0x00
#define APDS_9930_REG_Ch0DATAL 0x14 // Ch0 ADC Low data 0x00 #define APDS_9930_REG_Ch0DATAL 0x14 // Ch0 ADC Low data 0x00
#define APDS_9930_REG_Ch0DATAH 0x15 // Ch0 ADC High data 0x00 #define APDS_9930_REG_Ch0DATAH 0x15 // Ch0 ADC High data 0x00
#define APDS_9930_REG_Ch1DATAL 0x16 // Ch1 ADC Low data 0x00 #define APDS_9930_REG_Ch1DATAL 0x16 // Ch1 ADC Low data 0x00
@@ -124,13 +115,14 @@ esp_err_t apds_9930_read_word(uint8_t command, uint8_t* data_l,
} }
void ambient_light_fetch(void* arg) { void ambient_light_fetch(void* arg) {
ESP_LOGI(AMBIENT_LIGHT_TAG, "ambient_light_fetch");
esp_err_t error; esp_err_t error;
uint16_t als_ch0_raw; uint16_t als_ch0_raw;
uint16_t als_ch1_raw; uint16_t als_ch1_raw;
uint16_t proximity_raw; uint16_t proximity_raw;
char als_ch0_str[10]; char als_ch0_str[20];
char als_ch1_str[10]; char als_ch1_str[20];
char proximity_str[10]; char proximity_str[20];
uint8_t als_ch0_buffer[] = {0, 0}; uint8_t als_ch0_buffer[] = {0, 0};
uint8_t als_ch1_buffer[] = {0, 0}; uint8_t als_ch1_buffer[] = {0, 0};
uint8_t proximity_buffer[] = {0, 0}; uint8_t proximity_buffer[] = {0, 0};
@@ -164,30 +156,38 @@ void ambient_light_fetch(void* arg) {
als_ch0_raw); als_ch0_raw);
} }
// als ch1 // als ch1
error = apds_9930_read_word( // error = apds_9930_read_word(
APDS_9930_CMD_AUTO_INCREMENT | APDS_9930_REG_Ch1DATAL, // APDS_9930_CMD_AUTO_INCREMENT | APDS_9930_REG_Ch1DATAL,
&(als_ch1_buffer[1]), &(als_ch1_buffer[0])); // &(als_ch1_buffer[1]), &(als_ch1_buffer[0]));
if (error != ESP_OK) { // if (error != ESP_OK) {
ESP_LOGW(AMBIENT_LIGHT_TAG, "read failed. %x", error); // ESP_LOGW(AMBIENT_LIGHT_TAG, "read failed. %x", error);
} else { // } else {
als_ch1_raw = als_ch1_buffer[0] << 8 | als_ch1_buffer[1]; // als_ch1_raw = als_ch1_buffer[0] << 8 | als_ch1_buffer[1];
sprintf(als_ch1_str, "Ch1: % 5d ", als_ch1_raw); // sprintf(als_ch1_str, "Ch1: % 5d ", als_ch1_raw);
display_print8_str(8, 6, als_ch1_str); // display_print8_str(8, 6, als_ch1_str);
ESP_LOGD(AMBIENT_LIGHT_TAG, "ALS Ch1: %d, %x", als_ch1_raw, // ESP_LOGD(AMBIENT_LIGHT_TAG, "ALS Ch1: %d, %x", als_ch1_raw,
als_ch1_raw); // als_ch1_raw);
} // }
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(1000));
} }
display_fill_rect(0, 2, 128, 8, 0x00); display_fill_rect(0, 2, 128, 8, 0x00);
} }
void ambient_light_auto_fetch() { void ambient_light_auto_fetch() {
xTaskCreate(ambient_light_fetch, "ui_input_event", 2048, NULL, 10, NULL); if (is_apds_9930_online == 0) {
return;
}
xTaskCreate(ambient_light_fetch, "ambient-light", 2048, NULL, 10, NULL);
} }
void ambient_light_init() { void ambient_light_init() {
// esp_log_level_set(AMBIENT_LIGHT_TAG, ESP_LOG_DEBUG); if (is_apds_9930_online == 0) {
ESP_LOGI(AMBIENT_LIGHT_TAG, "APDS 9930 is offline");
return;
}
ESP_LOGI(AMBIENT_LIGHT_TAG, "Initializing APDS-9930");
// esp_log_level_set(AMBIENT_LIGHT_TAG, ESP_LOG_ERROR);
ESP_ERROR_CHECK(apds_9930_write(APDS_9930_CMD_REPEATED | APDS_9930_REG_ATIME, ESP_ERROR_CHECK(apds_9930_write(APDS_9930_CMD_REPEATED | APDS_9930_REG_ATIME,
APDS_9930_ATIME_VALUE)); APDS_9930_ATIME_VALUE));
ESP_ERROR_CHECK(apds_9930_write(APDS_9930_CMD_REPEATED | APDS_9930_REG_PTIME, ESP_ERROR_CHECK(apds_9930_write(APDS_9930_CMD_REPEATED | APDS_9930_REG_PTIME,

525
main/apds_9960.c Normal file
View File

@@ -0,0 +1,525 @@
#include <stdio.h>
#include <stdlib.h>
#include "ch1116.c"
#include "common.h"
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "i2c.c"
#define APDS_9960_INT_GPIO CONFIG_APDS_9960_INT_GPIO
// ===============================================
// Register addresses
// ===============================================
// 0x80 ENABLE R/W Enable states and interrupts 0x00
// 0x81 ATIME R/W ADC integration time 0xFF
// 0x83 WTIME R/W Wait time (non-gesture) 0xFF
// 0x84 AILTL R/W ALS interrupt low threshold low byte --
// 0x85 AILTH R/W ALS interrupt low threshold high byte --
// 0x86 AIHTL R/W ALS interrupt high threshold low byte 0x00
// 0x87 AIHTH R/W ALS interrupt high threshold high byte 0x00
// 0x89 PILT R/W Proximity interrupt low threshold 0x00
// 0x8B PIHT R/W Proximity interrupt high threshold 0x00
// 0x8C PERS R/W Interrupt persistence filters (non-gesture) 0x00
// 0x8D CONFIG1 R/W Configuration register one 0x60
// 0x8E PPULSE R/W Proximity pulse count and length 0x40
// 0x8F CONTROL R/W Gain control 0x00
// 0x90 CONFIG2 R/W Configuration register two 0x01
// 0x92 ID R Device ID ID
// 0x93 STATUS R Device status 0x00
// 0x94 CDATAL R Low byte of clear channel data 0x00
// 0x95 CDATAH R High byte of clear channel data 0x00
// 0x96 RDATAL R Low byte of red channel data 0x00
// 0x97 RDATAH R High byte of red channel data 0x00
// 0x98 GDATAL R Low byte of green channel data 0x00
// 0x99 GDATAH R High byte of green channel data 0x00
// 0x9A BDATAL R Low byte of blue channel data 0x00
// 0x9B BDATAH R High byte of blue channel data 0x00
// 0x9C PDATA R Proximity data 0x00
// 0x9D POFFSET_UR R/W Proximity offset for UP and RIGHT photodiodes 0x00
// 0x9E POFFSET_DL R/W Proximity offset for DOWN and LEFT photodiodes 0x00
// 0x9F CONFIG3 R/W Configuration register three 0x00
// 0xA0 GPENTH R/W Gesture proximity enter threshold 0x00
// 0xA1 GEXTH R/W Gesture exit threshold 0x00
// 0xA2 GCONF1 R/W Gesture configuration one 0x00
// 0xA3 GCONF2 R/W Gesture configuration two 0x00
// 0xA4 GOFFSET_U R/W Gesture UP offset register 0x00
// 0xA5 GOFFSET_D R/W Gesture DOWN offset register 0x00
// 0xA7 GOFFSET_L R/W Gesture LEFT offset register 0x00
// 0xA9 GOFFSET_R R/W Gesture RIGHT offset register 0x00
// 0xA6 GPULSE R/W Gesture pulse count and length 0x40
// 0xAA GCONF3 R/W Gesture configuration three 0x00
// 0xAB GCONF4 R/W Gesture configuration four 0x00
// 0xAE GFLVL R Gesture FIFO level 0x00
// 0xAF GSTATUS R Gesture status 0x00
// 0xE4 (1) IFORCE W Force interrupt 0x00
// 0xE5 (1) PICLEAR W Proximity interrupt clear 0x00
// 0xE6 (1) CICLEAR W ALS clear channel interrupt clear 0x00
// 0xE7 (1) AICLEAR W All non-gesture interrupts clear 0x00
// 0xFC GFIFO_U R Gesture FIFO UP value 0x00
// 0xFD GFIFO_D R Gesture FIFO DOWN value 0x00
// 0xFE GFIFO_L R Gesture FIFO LEFT value 0x00
// 0xFF GFIFO_R R Gesture FIFO RIGHT value 0x00
// ----------------------------------- 描述 默认值
#define APDS_9960_REG_ENABLE 0x80 // 状态和中断的启用 0x00
#define APDS_9960_REG_ATIME 0x81 // ADC 积分时间 0xff
#define APDS_9960_REG_WTIME 0x83 // 等待时间(非手势) 0xff
#define APDS_9960_REG_AILTL 0x84 // ALS 中断低阈值低字节 --
#define APDS_9960_REG_AILTH 0x85 // ALS 中断低阈值高字节 --
#define APDS_9960_REG_AIHTL 0x86 // ALS 中断高阈值低字节 0x00
#define APDS_9960_REG_AIHTH 0x87 // ALS 中断高阈值高字节 0x00
#define APDS_9960_REG_PILT 0x89 // 接近中断低阈值 0x00
#define APDS_9960_REG_PIHT 0x8B // 接近中断高阈值 0x00
#define APDS_9960_REG_PERS 0x8C // 中断持续性过滤器(非手势) 0x00
#define APDS_9960_REG_CONFIG1 0x8D // 配置寄存器一 0x60
#define APDS_9960_REG_PPULSE 0x8E // 接近脉冲计数和长度 0x40
#define APDS_9960_REG_CONTROL 0x8F // 增益控制 0x00
#define APDS_9960_REG_CONFIG2 0x90 // 配置寄存器二 0x01
#define APDS_9960_REG_ID 0x92 // 设备 ID ID
#define APDS_9960_REG_STATUS 0x93 // 设备状态 0x00
#define APDS_9960_REG_CDATAL 0x94 // 清除通道数据低字节 0x00
#define APDS_9960_REG_CDATAH 0x95 // 清除通道数据高字节 0x00
#define APDS_9960_REG_RDATAL 0x96 // 红色通道数据低字节 0x00
#define APDS_9960_REG_RDATAH 0x97 // 红色通道数据高字节 0x00
#define APDS_9960_REG_GDATAL 0x98 // 绿色通道数据低字节 0x00
#define APDS_9960_REG_GDATAH 0x99 // 绿色通道数据高字节 0x00
#define APDS_9960_REG_BDATAL 0x9A // 蓝色通道数据低字节 0x00
#define APDS_9960_REG_BDATAH 0x9B // 蓝色通道数据高字节 0x00
#define APDS_9960_REG_PDATA 0x9C // 接近数据 0x00
#define APDS_9960_REG_POFFSET_UR 0x9D // 接近偏移量上和右 0x00
#define APDS_9960_REG_POFFSET_DL 0x9E // 接近偏移量下和左 0x00
#define APDS_9960_REG_CONFIG3 0x9F // 配置寄存器三 0x00
#define APDS_9960_REG_GPENTH 0xA0 // 手势进入阈值 0x28
#define APDS_9960_REG_GEXTH 0xA1 // 手势退出阈值 0x1E
#define APDS_9960_REG_GCONF1 0xA2 // 手势配置寄存器一 0x40
#define APDS_9960_REG_GCONF2 0xA3 // 手势配置寄存器二 0x66
#define APDS_9960_REG_GOFFSET_U 0xA4 // 手势偏移量上 0x00
#define APDS_9960_REG_GOFFSET_D 0xA5 // 手势偏移量下 0x00
#define APDS_9960_REG_GOFFSET_L 0xA7 // 手势偏移量左 0x00
#define APDS_9960_REG_GOFFSET_R 0xA9 // 手势偏移量右 0x00
#define APDS_9960_REG_GPULSE 0xA6 // 手势脉冲计数和长度 0xC5
#define APDS_9960_REG_GCONF3 0xAA // 手势配置寄存器三 0x00
#define APDS_9960_REG_GCONF4 0xAB // 手势配置寄存器四 0x00
#define APDS_9960_REG_GFLVL 0xAE // 手势 FIFO 级别 0x00
#define APDS_9960_REG_GSTATUS 0xAF // 手势状态 0x00
#define APDS_9960_REG_IFORCE 0xE4 // 强制中断 0x00
#define APDS_9960_REG_PICLEAR 0xE5 // 清除接近中断 0x00
#define APDS_9960_REG_CICLEAR 0xE6 // 清除 ALS 中断 0x00
#define APDS_9960_REG_AICLEAR 0xE7 // 清除手势中断 0x00
#define APDS_9960_REG_GFIFO_U 0xFC // 手势 FIFO 数据上 0x00
#define APDS_9960_REG_GFIFO_D 0xFD // 手势 FIFO 数据下 0x00
#define APDS_9960_REG_GFIFO_L 0xFE // 手势 FIFO 数据左 0x00
#define APDS_9960_REG_GFIFO_R 0xFF // 手势 FIFO 数据右 0x00
#define APDS_9960_GVALID 0b00000001 // GVALID
#define APDS_9960_GFOV 0b00000010 // GFOV
#define APDS_9960_PINT 0x20 // PINT
#define APDS_9960_AINT 0x10 // AINT
#define APDS_9960_GINT 0x04 // GINT
// 50 mA LED, Reserved, 2x PGAIN, 4x ALS/Cain GAIN
#define APDS_9960_CONTROL_VALUE 0b00001010
// Enable Gesture, Proximity, ALS, Power
// X, GEN, PIEN, AIEN, WEN, PEN, AEN, PON
#define APDS_9960_ENABLE_VALUE 0b01100111
#define APDS_9960_TAG "APDS-9960"
static QueueHandle_t apds_9960_int_evt_queue = NULL;
static int64_t last_apds_9960_int_time = 0;
esp_err_t apds_9960_write_empty(uint8_t command) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, command, ACK_CHECK_DIS);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "write failed. %d", error);
}
return error;
}
esp_err_t apds_9960_write(uint8_t command, uint8_t data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, command, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "write failed. %d", error);
}
return error;
}
esp_err_t apds_9960_read_byte(uint8_t command, uint8_t* data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, command, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_READ,
ACK_CHECK_EN);
i2c_master_read_byte(cmd, data, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
}
return error;
}
// read word
esp_err_t apds_9960_read_word(uint8_t command, uint16_t* data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, command, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_READ,
ACK_CHECK_EN);
i2c_master_read_byte(cmd, (uint8_t*)data, ACK_VAL);
i2c_master_read_byte(cmd, (uint8_t*)data + 1, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
}
return error;
}
// read bytes by length
esp_err_t apds_9960_read_bytes_len(uint8_t command, uint8_t* data,
uint8_t len) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, command, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, APDS_9960_ADDRESS << 1 | I2C_MASTER_READ,
ACK_CHECK_EN);
for (int i = 0; i < len; i++) {
if (i == len - 1) {
i2c_master_read_byte(cmd, (uint8_t*)data + i, NACK_VAL);
} else {
i2c_master_read_byte(cmd, (uint8_t*)data + i, ACK_VAL);
}
}
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 100 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
}
return error;
}
void apds_9960_clear_all_int(void) {
ESP_LOGI(APDS_9960_TAG, "apds_9960_clear_all_int");
ESP_ERROR_CHECK_WITHOUT_ABORT(apds_9960_write_empty(APDS_9960_REG_AICLEAR));
ESP_ERROR_CHECK_WITHOUT_ABORT(apds_9960_write(APDS_9960_REG_GCONF4, 0x06));
}
void apds_9960_fetch(void* arg) {
ESP_LOGI(APDS_9960_TAG, "apds_9960_fetch");
esp_err_t error;
uint16_t red_raw;
uint16_t green_raw;
uint16_t blue_raw;
uint16_t clear_raw;
uint8_t byte_buffer;
uint8_t gesture_status_raw;
uint8_t status_raw;
char red_str[20];
char green_str[20];
char blue_str[20];
char clear_str[20];
char status_str[20];
uint8_t interrupt = 0;
display_fill_rect(0, 2, 128, 8, 0x00);
for (;;) {
if (last_apds_9960_int_time + 1000000 < esp_timer_get_time()) {
interrupt = gpio_get_level(APDS_9960_INT_GPIO);
if (interrupt == 1 ||
last_apds_9960_int_time + 10000000 < esp_timer_get_time()) {
apds_9960_clear_all_int();
}
}
// clear
error = apds_9960_read_word(APDS_9960_REG_CDATAL, &clear_raw);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
} else {
sprintf(clear_str, "C:% 5d", clear_raw);
display_print8_str(64, 6, clear_str);
ESP_LOGD(APDS_9960_TAG, "Clear: %d", clear_raw);
}
// red
error = apds_9960_read_word(APDS_9960_REG_RDATAL, &red_raw);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
} else {
sprintf(red_str, "R:% 5d", red_raw);
display_print8_str(0, 4, red_str);
ESP_LOGD(APDS_9960_TAG, "Red: %d", red_raw);
}
// green
error = apds_9960_read_word(APDS_9960_REG_GDATAL, &green_raw);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
} else {
sprintf(green_str, "G:% 5d", green_raw);
display_print8_str(64, 4, green_str);
ESP_LOGD(APDS_9960_TAG, "Green: %d", green_raw);
}
// blue
error = apds_9960_read_word(APDS_9960_REG_BDATAL, &blue_raw);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read failed. %d", error);
} else {
sprintf(blue_str, "B:% 5d", blue_raw);
display_print8_str(0, 6, blue_str);
ESP_LOGD(APDS_9960_TAG, "Blue: %d", blue_raw);
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
display_fill_rect(0, 2, 128, 8, 0x00);
}
void apds_9960_auto_fetch() {
if (is_apds_9960_online == 0) {
return;
}
xTaskCreate(apds_9960_fetch, "APDS-9960-fetch", 2048, NULL, 10, NULL);
}
void apds_9960_read_gesture() {
ESP_LOGI(APDS_9960_TAG, "apes_9960_gesture_fetch");
uint8_t byte_buffer;
esp_err_t error;
uint32_t gesture_values_raw_arr[32];
char gesture_values_str_arr[20];
error = apds_9960_read_byte(APDS_9960_REG_GSTATUS, &byte_buffer);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read APDS_9960_REG_GSTATUS failed. %d", error);
return;
}
ESP_LOGD(APDS_9960_TAG, "APDS-9960 interrupt. status: %x", byte_buffer);
if (!(byte_buffer & APDS_9960_GVALID)) {
ESP_LOGI(APDS_9960_TAG, "Gesture no valid");
return;
}
ESP_LOGD(APDS_9960_TAG, "Gesture interrupt");
error = apds_9960_read_byte(APDS_9960_REG_GFLVL, &byte_buffer);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read APDS_9960_REG_GFLVL failed. %d", error);
apds_9960_write(APDS_9960_REG_GCONF4, 0x06);
return;
}
if (byte_buffer < 4) {
ESP_LOGD(APDS_9960_TAG, "Gesture FIFO level too low: %d", byte_buffer);
apds_9960_write(APDS_9960_REG_GCONF4, 0x06);
return;
}
ESP_LOGD(APDS_9960_TAG, "Gesture FIFO Level: %d", byte_buffer);
error = apds_9960_read_bytes_len(
APDS_9960_REG_GFIFO_U, (uint8_t*)gesture_values_raw_arr, byte_buffer * 4);
apds_9960_write(APDS_9960_REG_GCONF4, 0x06);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read APDS_9960_REG_GFIFO(len: %d) failed. % d",
byte_buffer * 4, error);
} else {
int16_t before_ud = 0, before_lr = 0, after_ud = 0, after_lr = 0;
int16_t u = 0, d = 0, l = 0, r = 0;
uint8_t last_2_index = byte_buffer - 2;
// head 2
for (int i = 0; i < 2; i++) {
u = gesture_values_raw_arr[i] & 0xff;
d = gesture_values_raw_arr[i] >> 8 & 0xff;
l = gesture_values_raw_arr[i] >> 16 & 0xff;
r = gesture_values_raw_arr[i] >> 24 & 0xff;
before_ud += (u - d) * 100 / u + d;
before_lr += (l - r) * 100 / l + r;
}
// last 2
for (int i = last_2_index; i < byte_buffer; i++) {
u = gesture_values_raw_arr[i] & 0xff;
d = gesture_values_raw_arr[i] >> 8 & 0xff;
l = gesture_values_raw_arr[i] >> 16 & 0xff;
r = gesture_values_raw_arr[i] >> 24 & 0xff;
after_ud += (u - d) * 100 / u + d;
after_lr += (l - r) * 100 / l + r;
}
for (int i = 0; i < byte_buffer; i++) {
if (i < 2) {
u = gesture_values_raw_arr[i] & 0xff;
d = gesture_values_raw_arr[i] >> 8 & 0xff;
l = gesture_values_raw_arr[i] >> 16 & 0xff;
r = gesture_values_raw_arr[i] >> 24 & 0xff;
before_ud += (u - d) * 100 / u + d;
before_lr += (l - r) * 100 / l + r;
} else if (i >= last_2_index) {
u = gesture_values_raw_arr[i] & 0xff;
d = gesture_values_raw_arr[i] >> 8 & 0xff;
l = gesture_values_raw_arr[i] >> 16 & 0xff;
r = gesture_values_raw_arr[i] >> 24 & 0xff;
after_ud += (u - d) * 100 / u + d;
after_lr += (l - r) * 100 / l + r;
}
}
printf("Δud: %d, Δlr: %d \n", after_ud - before_ud, after_lr - before_lr);
display_fill_rect(0, 0, 128, 2, 0x00);
if (abs(after_ud - before_ud) * 2 > abs(after_lr - before_lr)) {
if (after_ud - before_ud < -80) {
display_print8_str(0, 0, "Gesture: up");
} else if (after_ud - before_ud > 80) {
display_print8_str(0, 0, "Gesture: down");
}
} else {
if (after_lr - before_lr < -120) {
display_print8_str(0, 0, "Gesture: left");
} else if (after_lr - before_lr > 120) {
display_print8_str(0, 0, "Gesture: right");
}
}
// display_print8_str(0, 0, gesture_str);
}
}
void apds_9960_read_proximity() {
uint8_t proximity_raw;
char proximity_str[20];
esp_err_t error;
// Proximity
error = apds_9960_read_byte(APDS_9960_REG_PDATA, &proximity_raw);
if (error != ESP_OK) {
ESP_LOGW(APDS_9960_TAG, "read proximity failed. %x", error);
} else {
ESP_LOGD(APDS_9960_TAG, "Prox: % 5d ", proximity_raw);
sprintf(proximity_str, "Prox: % 5d ", proximity_raw);
display_print8_str(8, 2, proximity_str);
}
}
void apds_9960_int_handler(void* arg) {
xQueueSendFromISR(apds_9960_int_evt_queue, &arg, NULL);
}
void apds_9960_int_evt_handler() {
if (is_apds_9960_online == 0) {
return;
}
apds_9960_clear_all_int();
esp_err_t error;
uint8_t status_raw;
while (xQueueReceive(apds_9960_int_evt_queue, NULL, portMAX_DELAY)) {
last_apds_9960_int_time = esp_timer_get_time();
ESP_ERROR_RETRY(apds_9960_read_byte(APDS_9960_REG_STATUS, &status_raw), 10);
ESP_ERROR_RETRY(apds_9960_write_empty(APDS_9960_REG_AICLEAR), 10);
ESP_LOGD(
APDS_9960_TAG, "[apds_9960_int_evt_handler] status %d%d%d%d %d%d%d%d",
(status_raw >> 7) & 1, (status_raw >> 6) & 1, (status_raw >> 5) & 1,
(status_raw >> 4) & 1, (status_raw >> 3) & 1, (status_raw >> 2) & 1,
(status_raw >> 1) & 1, status_raw & 1);
if (status_raw & APDS_9960_PINT) {
apds_9960_read_proximity();
}
if (status_raw & APDS_9960_GINT) {
apds_9960_read_gesture();
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
}
void apds_9960_init() {
if (is_apds_9960_online == 0) {
ESP_LOGI(APDS_9960_TAG, "APDS-9960 is offline");
return;
}
ESP_LOGI(APDS_9960_TAG, "Initializing APDS-9960");
// esp_log_level_set(APDS_9960_TAG, ESP_LOG_DEBUG);
gpio_config_t io_conf = {};
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 0;
io_conf.pull_down_en = 0;
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = 1ULL << APDS_9960_INT_GPIO;
gpio_config(&io_conf);
gpio_isr_handler_add(APDS_9960_INT_GPIO, apds_9960_int_handler, NULL);
// 环境光 ADC 积分时间
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_ATIME, 246)); // 27.8ms
// 增益
ESP_ERROR_CHECK(
apds_9960_write(APDS_9960_REG_CONTROL, APDS_9960_CONTROL_VALUE));
// enable gesture interrupt
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_GCONF4, 0x06));
// 累积的手势数据达到 4 组时触发中断
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_GCONF1, 0x40));
// Gesture Enter Threshold
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_GPENTH, 3));
// Gesture Exit Threshold
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_GEXTH, 3));
// Gesture Drive Strength
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_GCONF2, 0b0110000));
// // set wait time
// ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_WTIME, 171));
// set interrupt persistence
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_PERS, 0x44));
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_PILT, 0x80));
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_PIHT, 0x40));
ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_CONFIG2, 0x80));
// // enable sleep after interrupt
// ESP_ERROR_CHECK(apds_9960_write(APDS_9960_REG_CONFIG3, 0x10));
ESP_ERROR_CHECK(
apds_9960_write(APDS_9960_REG_ENABLE, APDS_9960_ENABLE_VALUE));
apds_9960_int_evt_queue = xQueueCreate(10, NULL);
xTaskCreate(apds_9960_int_evt_handler, "apds_9960_gesture_fetch", 2048, NULL,
10, NULL);
}

183
main/app_icon_8.c Normal file
View File

@@ -0,0 +1,183 @@
/*******************************************************************************
* Size: 8 px
* Bpp: 1
* Opts:
******************************************************************************/
#pragma once
#include "lvgl.h"
#ifndef APP_ICON_8
#define APP_ICON_8 1
#endif
#if APP_ICON_8
/*-----------------
* BITMAPS
*----------------*/
/*Store the image of the glyphs*/
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
/* U+E1CB "" */
0x38, 0x31, 0x88, 0xfa, 0x49, 0x54, 0x44, 0x10, 0xf8,
/* U+E1CC "" */
0x80, 0x1f, 0xa, 0x22, 0x6b, 0xd4, 0x44, 0xd0, 0xc8, 0x1,
/* U+E29E "" */
0xe, 0x4, 0xa8, 0x32, 0x18, 0xe, 0xd, 0x8c, 0x7c,
/* U+F1EB "" */
0x1e, 0x18, 0x68, 0x4, 0x78, 0x21, 0x0, 0x0, 0xc0, 0x0,
/* U+F6AA "" */
0xc0,
/* U+F6AB "" */
0x79, 0x8, 0x1, 0x80,
/* U+F354 "" */
0x38, 0x28, 0x28, 0x28, 0xee, 0x82, 0x44, 0x28, 0x10,
/* U+F357 "" */
0x8, 0x14, 0x22, 0x41, 0x77, 0x14, 0x14, 0x14, 0x1c,
/* U+F354 "" */
0x31, 0x45, 0x37, 0x85, 0x23, 0x0,
/* U+F357 "" */
0x31, 0x28, 0x7b, 0x30, 0xc3, 0x0};
/*---------------------
* GLYPH DESCRIPTION
*--------------------*/
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 0,
.adv_w = 0,
.box_w = 0,
.box_h = 0,
.ofs_x = 0,
.ofs_y = 0} /* id = 0 reserved */,
{.bitmap_index = 0,
.adv_w = 160,
.box_w = 10,
.box_h = 7,
.ofs_x = 0,
.ofs_y = 0},
{.bitmap_index = 9,
.adv_w = 160,
.box_w = 10,
.box_h = 8,
.ofs_x = 0,
.ofs_y = -1},
{.bitmap_index = 19,
.adv_w = 128,
.box_w = 9,
.box_h = 8,
.ofs_x = 0,
.ofs_y = -1},
{.bitmap_index = 28,
.adv_w = 160,
.box_w = 10,
.box_h = 8,
.ofs_x = 0,
.ofs_y = -1},
{.bitmap_index = 38,
.adv_w = 32,
.box_w = 2,
.box_h = 2,
.ofs_x = 4,
.ofs_y = -1},
{.bitmap_index = 39,
.adv_w = 112,
.box_w = 7,
.box_h = 4,
.ofs_x = 2,
.ofs_y = 0},
{.bitmap_index = 48,
.adv_w = 96,
.box_w = 6,
.box_h = 7,
.ofs_x = 0,
.ofs_y = 0},
{.bitmap_index = 54,
.adv_w = 96,
.box_w = 6,
.box_h = 7,
.ofs_x = 0,
.ofs_y = -1}
};
/*---------------------
* CHARACTER MAPPING
*--------------------*/
static const uint16_t unicode_list_0[] = {0x0, 0x1, 0xd3,
0x1020, 0x14df, 0x14e0};
/*Collect the unicode lists and glyph_id offsets*/
static const lv_font_fmt_txt_cmap_t cmaps[] = {
{.range_start = 57803,
.range_length = 5345,
.glyph_id_start = 1,
.unicode_list = unicode_list_0,
.glyph_id_ofs_list = NULL,
.list_length = 6,
.type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY}};
/*--------------------
* ALL CUSTOM DATA
*--------------------*/
#if LV_VERSION_CHECK(8, 0, 0)
/*Store all the custom data of the font*/
static lv_font_fmt_txt_glyph_cache_t cache;
static const lv_font_fmt_txt_dsc_t font_dsc = {
#else
static lv_font_fmt_txt_dsc_t font_dsc = {
#endif
.glyph_bitmap = glyph_bitmap,
.glyph_dsc = glyph_dsc,
.cmaps = cmaps,
.kern_dsc = NULL,
.kern_scale = 0,
.cmap_num = 1,
.bpp = 1,
.kern_classes = 0,
.bitmap_format = 0,
#if LV_VERSION_CHECK(8, 0, 0)
.cache = &cache
#endif
};
/*-----------------
* PUBLIC FONT
*----------------*/
/*Initialize a public general font descriptor*/
#if LV_VERSION_CHECK(8, 0, 0)
const lv_font_t app_icon_8 = {
#else
lv_font_t app_icon_8 = {
#endif
.get_glyph_dsc =
lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
.get_glyph_bitmap =
lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
.line_height = 8, /*The maximum line height required by the font*/
.base_line = 1, /*Baseline measured from the bottom of the line*/
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
.subpx = LV_FONT_SUBPX_NONE,
#endif
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
.underline_position = -1,
.underline_thickness = 0,
#endif
.dsc = &font_dsc /*The custom font data. Will be accessed by
`get_glyph_bitmap/dsc` */
};
#endif /*#if APP_ICON_8*/

19
main/app_nvs.c Normal file
View File

@@ -0,0 +1,19 @@
#pragma once
#include <stdio.h>
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs.h"
#include "nvs_flash.h"
void app_nvs_init() {
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES ||
err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}

87
main/asr_pro.c Normal file
View File

@@ -0,0 +1,87 @@
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "light.c"
#include "string.h"
static const int RX_BUF_SIZE = 1024;
#define TXD_PIN (GPIO_NUM_12)
#define RXD_PIN (GPIO_NUM_13)
int sendData(const char *logName, const char *data) {
const int len = strlen(data);
const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
ESP_LOGI(logName, "Wrote %d bytes", txBytes);
return txBytes;
}
static void tx_task(void *arg) {
static const char *TX_TASK_TAG = "TX_TASK";
esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
while (1) {
sendData(TX_TASK_TAG, "Hello world");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
static void rx_task(void *arg) {
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t *data = (uint8_t *)malloc(RX_BUF_SIZE + 1);
uint8_t tx_buff[4] = {0};
while (1) {
const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE,
100 / portTICK_PERIOD_MS);
if (rxBytes > 0) {
data[rxBytes] = 0;
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
if (data[0] == 0x11 && rxBytes >= 2) {
if (data[1] <= 6) {
light_mode = data[1];
}
tx_buff[0] = data[0];
tx_buff[1] = light_mode;
uart_write_bytes(UART_NUM_1, tx_buff, 2);
} else if (data[0] == 0x12 && rxBytes >= 2) {
tx_buff[0] = 0x12;
tx_buff[1] = data[1];
led_strip_set_brightness(data[1]);
uart_write_bytes(UART_NUM_1, tx_buff, 2);
} else if (data[0] == 0x13 && rxBytes >= 2) {
tx_buff[0] = 0x12;
tx_buff[1] = display_ambient_lighting_level + (int8_t)data[1];
led_strip_set_brightness(tx_buff[1]);
uart_write_bytes(UART_NUM_1, tx_buff, 2);
}
}
}
free(data);
}
void asr_pro_init() {
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
// We won't use a buffer for sending data.
uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(UART_NUM_1, &uart_config);
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE);
xTaskCreate(rx_task, "uart_rx_task", 1024 * 2, NULL, configMAX_PRIORITIES,
NULL);
// xTaskCreate(tx_task, "uart_tx_task", 1024 * 2, NULL, configMAX_PRIORITIES -
// 1,
// NULL);
}

269
main/ch1116.c Normal file
View File

@@ -0,0 +1,269 @@
#pragma once
#include <string.h>
#include "driver/gpio.h"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/task.h"
#include "sdkconfig.h" // generated by "make menuconfig"
#define CH1116_TAG "CH1116"
#define SDA_PIN GPIO_NUM_4
#define SCL_PIN GPIO_NUM_5
#define CH1116_WIDTH 128
#define CH1116_HEIGHT 64
#define CH1116_ADDRESS 0x3C // 011110+SA0 - 0x3C or 0x3D
// CH1116 Control
#define CH1116_CONTROL_BYTE_COMMAND_SINGLE 0x80
#define CH1116_CONTROL_BYTE_COMMAND_STREAM 0x00
#define CH1116_CONTROL_BYTE_DATA_SINGLE 0xC0
#define CH1116_CONTROL_BYTE_DATA_STREAM 0x40
// CH1116 Config Commands
#define CH1116_DISPLAY_OFF 0xAE // #13
#define CH1116_LOWER_COLUMN_ADDRESS 0x02 // #1 0x02-0x0F
#define CH1116_HIGHER_COLUMN_ADDRESS 0x10 // #2 0x10-0x1F
#define CH1116_DISPLAY_START_LINE 0x40 // #3
#define CH1116_PAGE_ADDRESS 0xB0 // #18 0xB0-0xB7
#define CH1116_CONTRACT_CONTROL 0x81 // #10
#define CH1116_CONTRACT_CONTROL_128 0xCF // #10
#define CH1116_SEGMENT_REMAP 0xA1 // #12 ADC=1 (right)
#define CH1116_NORMAL_DISPLAY 0xA6 // #14
#define CH1116_MULTIPLEX_RATIO 0xA8 // #15
#define CH1116_MULTIPLEX_RATIO_1_64 0x3F
// VCC Generated by Internal DC/DC Circuit
#define CH1116_CHARGE_PUMP_ENABLE 0xAD // #16
#define CH1116_CHARGE_PUMP_ENABLE_INTERNAL_VCC 0x8B // #16
#define CH1116_CHARGE_PUMP_VOLTAGE_9V 0x33 // #8
#define CH1116_COM_SCAN_DIRECTION 0xC8 // #19
#define CH1116_DISPLAY_OFFSET 0xD3 // #20
#define CH1116_DISPLAY_OFFSET_0 0x00 // #20
#define CH1116_SET_OSC_DIVISION 0xD5 // #21
#define CH1116_SET_OSC_DIVISION_1 0x80 // #21
#define CH1116_SET_PRE_CHARGE_PERIOD 0xD9 // #22
#define CH1116_SET_PRE_CHARGE_PERIOD_1 0x1F // #22
#define CH1116_SET_COM_PINS 0xDA // #23
#define CH1116_SET_COM_PINS_1 0x12 // #23
#define CH1116_SET_VCOMH 0xDB // #24
#define CH1116_SET_VCOMH_1 0x40 // #24
#define CH1116_DISPLAY_ON 0xAF // #13
void ch1116_init() {
esp_err_t err;
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true);
i2c_master_write_byte(cmd, CH1116_DISPLAY_OFF, true);
i2c_master_write_byte(cmd, CH1116_LOWER_COLUMN_ADDRESS, true);
i2c_master_write_byte(cmd, CH1116_HIGHER_COLUMN_ADDRESS, true);
i2c_master_write_byte(cmd, CH1116_DISPLAY_START_LINE, true);
i2c_master_write_byte(cmd, CH1116_PAGE_ADDRESS, true);
i2c_master_write_byte(cmd, CH1116_CONTRACT_CONTROL, true);
i2c_master_write_byte(cmd, CH1116_CONTRACT_CONTROL_128, true);
i2c_master_write_byte(cmd, CH1116_SEGMENT_REMAP, true);
i2c_master_write_byte(cmd, CH1116_NORMAL_DISPLAY, true);
i2c_master_write_byte(cmd, CH1116_MULTIPLEX_RATIO, true);
i2c_master_write_byte(cmd, CH1116_MULTIPLEX_RATIO_1_64, true);
i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_ENABLE, true);
i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_ENABLE_INTERNAL_VCC, true);
i2c_master_write_byte(cmd, CH1116_CHARGE_PUMP_VOLTAGE_9V, true);
i2c_master_write_byte(cmd, CH1116_COM_SCAN_DIRECTION, true);
i2c_master_write_byte(cmd, CH1116_DISPLAY_OFFSET, true);
i2c_master_write_byte(cmd, CH1116_DISPLAY_OFFSET_0, true);
i2c_master_write_byte(cmd, CH1116_SET_OSC_DIVISION, true);
i2c_master_write_byte(cmd, CH1116_SET_OSC_DIVISION_1, true);
i2c_master_write_byte(cmd, CH1116_SET_PRE_CHARGE_PERIOD, true);
i2c_master_write_byte(cmd, CH1116_SET_PRE_CHARGE_PERIOD_1, true);
i2c_master_write_byte(cmd, CH1116_SET_COM_PINS, true);
i2c_master_write_byte(cmd, CH1116_SET_COM_PINS_1, true);
i2c_master_write_byte(cmd, CH1116_SET_VCOMH, true);
i2c_master_write_byte(cmd, CH1116_SET_VCOMH_1, true);
i2c_master_write_byte(cmd, CH1116_DISPLAY_ON, true);
i2c_master_stop(cmd);
err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
if (err == ESP_OK) {
ESP_LOGI(CH1116_TAG, "OLED configured successfully");
} else {
ESP_LOGE(CH1116_TAG, "OLED configuration failed. code: %s",
esp_err_to_name(err));
}
i2c_cmd_link_delete(cmd);
}
void task_ch1116_display_pattern(void *ignore) {
i2c_cmd_handle_t cmd;
for (uint8_t i = 0; i < 8; i++) {
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_SINGLE, true);
i2c_master_write_byte(cmd, 0xB0 | i, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true);
for (uint8_t j = 0; j < 132; j++) {
i2c_master_write_byte(cmd, 0xFF >> (j % 8), true);
}
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
}
void task_ch1116_display_clear(void *ignore) {
i2c_cmd_handle_t cmd;
uint8_t zero[132];
memset(zero, 0, 132);
for (uint8_t i = 0; i < 8; i++) {
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_SINGLE, true);
i2c_master_write_byte(cmd, 0xB0 | i, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true);
i2c_master_write(cmd, zero, 132, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true);
i2c_master_write_byte(cmd, 0x00, true); // reset column
i2c_master_write_byte(cmd, 0x10, true);
i2c_master_write_byte(cmd, 0xB0, true); // reset page
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
static esp_err_t ch1116_draw_bitmap(int x_start, int y_start, int x_end,
int y_end, const void *color_data) {
assert((x_start < x_end) && (y_start < y_end) &&
"start position must be smaller than end position");
// one page contains 8 rows (COMs)
uint8_t page_start = y_start / 8;
uint8_t page_end = y_end / 8;
// define an area of frame memory where MCU can access
esp_err_t err;
i2c_cmd_handle_t cmd;
uint8_t *color_data_ptr = (uint8_t *)color_data;
uint16_t page_data_size = (x_end - x_start + 1);
// ESP_LOGI(CH1116_TAG, "y_start: %d, y_end: %d, page_start: %d, page_end:
// %d",
// y_start, y_end, page_start, page_end);
// ESP_LOGI(CH1116_TAG, "x_start: %d, x_end: %d, page_data_size: %d", x_start,
// x_end, page_data_size);
for (int page = page_start; page <= page_end; page++) {
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true);
// set cursor position
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_COMMAND_STREAM, true);
i2c_master_write_byte(cmd, CH1116_LOWER_COLUMN_ADDRESS | (x_start & 0x0f),
true);
i2c_master_write_byte(cmd, CH1116_HIGHER_COLUMN_ADDRESS | (x_start >> 4),
true);
i2c_master_write_byte(cmd, CH1116_PAGE_ADDRESS | (page & 0x0f), true);
i2c_master_stop(cmd);
err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
if (err != ESP_OK) {
i2c_cmd_link_delete(cmd);
return err;
}
i2c_cmd_link_delete(cmd);
// write page data
cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (CH1116_ADDRESS << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, CH1116_CONTROL_BYTE_DATA_STREAM, true);
i2c_master_write(cmd, color_data_ptr, page_data_size, true);
i2c_master_stop(cmd);
err = i2c_master_cmd_begin(I2C_NUM_0, cmd, 10 / portTICK_PERIOD_MS);
// ESP_LOG_BUFFER_HEXDUMP(CH1116_TAG, color_data_ptr, page_data_size,
// ESP_LOG_INFO);
color_data_ptr += page_data_size;
if (err != ESP_OK) {
i2c_cmd_link_delete(cmd);
return err;
}
i2c_cmd_link_delete(cmd);
}
return ESP_OK;
}
void ch1116_main(void) {
ch1116_init();
task_ch1116_display_pattern(NULL);
// vTaskDelay(1000 / portTICK_PERIOD_MS);
// task_ch1116_display_clear(NULL);
}

118
main/ci_03t.c Normal file
View File

@@ -0,0 +1,118 @@
#pragma once
#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "light.c"
#include "string.h"
static const int UART_1_RX_BUF_SIZE = 1024;
#define CI_03T_TXD_PIN (CONFIG_UART_TX)
#define CI_03T_RXD_PIN (CONFIG_UART_RX)
#define CI_03T_UART_NUM (CONFIG_UART_NUM)
#define SEND_DATA_FRAME_HEAD \
(uint8_t[]) { 0xaa, 0x55 }
#define SEND_DATA_FRAME_TAIL \
(uint8_t[]) { 0x55, 0xaa }
#define CI_O3T_LOG_TAG "CI_03T"
// emtpy buffer for sending data
uint8_t ci_03t_tx_buffer[20] = {0};
int ci_03t_send_data(const uint8_t *data, uint8_t data_len) {
memcpy(ci_03t_tx_buffer, SEND_DATA_FRAME_HEAD, sizeof(SEND_DATA_FRAME_HEAD));
memcpy(ci_03t_tx_buffer + sizeof(SEND_DATA_FRAME_HEAD), data, data_len);
memcpy(ci_03t_tx_buffer + sizeof(SEND_DATA_FRAME_HEAD) + data_len,
SEND_DATA_FRAME_TAIL, sizeof(SEND_DATA_FRAME_TAIL));
const int len =
sizeof(SEND_DATA_FRAME_HEAD) + data_len + sizeof(SEND_DATA_FRAME_TAIL);
const int txBytes = uart_write_bytes(CI_03T_UART_NUM, ci_03t_tx_buffer, len);
ESP_LOGI(CI_O3T_LOG_TAG, "Wrote %d bytes", txBytes);
ESP_LOG_BUFFER_HEXDUMP(CI_O3T_LOG_TAG, ci_03t_tx_buffer, len, ESP_LOG_INFO);
return txBytes;
}
int sendData(const char *data) {
const int len = strlen(data);
const int txBytes = uart_write_bytes(CI_03T_UART_NUM, data, len);
ESP_LOGI("TEST", "Wrote %d bytes", txBytes);
return txBytes;
}
static void tx_task(void *arg) {
static const char *TX_TASK_TAG = "TX_TASK";
esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
while (1) {
vTaskDelay(100 / portTICK_PERIOD_MS);
// sendData(TX_TASK_TAG, "Hello world");
}
}
static void rx_task(void *arg) {
static const char *RX_TASK_TAG = "RX_TASK";
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
uint8_t *data = (uint8_t *)malloc(UART_1_RX_BUF_SIZE + 1);
uint8_t tx_buff[4] = {0};
while (1) {
const int rxBytes = uart_read_bytes(
CI_03T_UART_NUM, data, UART_1_RX_BUF_SIZE, 100 / portTICK_PERIOD_MS);
if (rxBytes > 0) {
data[rxBytes] = 0;
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
if (data[0] == 0x98 && data[1] == 0x77 && data[rxBytes - 1] == 0x98 &&
data[rxBytes - 2] == 0x77) {
if (data[2] == 0x10) { // 氛围灯模式
if (data[3] == 0x02) { // 开灯、正常模式
tx_buff[0] = 0x05;
led_strip_set_brightness(150);
ci_03t_send_data(tx_buff, 1);
continue;
}
if (data[3] == 0x01) { // 开灯、正常模式
tx_buff[0] = 0x06;
led_strip_set_brightness(20);
ci_03t_send_data(tx_buff, 1);
continue;
}
if (data[3] == 0x00) { // 关灯
led_strip_set_brightness(0);
tx_buff[0] = 0x04;
ci_03t_send_data(tx_buff, 1);
continue;
}
}
ESP_LOGW(RX_TASK_TAG, "Unknow command");
} else {
ESP_LOGW(RX_TASK_TAG, "Invalid data");
}
}
}
free(data);
}
void ci_03t_init() {
const uart_config_t uart_config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_ODD,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_APB,
};
// We won't use a buffer for sending data.
uart_driver_install(CI_03T_UART_NUM, UART_1_RX_BUF_SIZE * 2, 0, 0, NULL, 0);
uart_param_config(CI_03T_UART_NUM, &uart_config);
uart_set_pin(CI_03T_UART_NUM, CI_03T_TXD_PIN, CI_03T_RXD_PIN,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
xTaskCreate(rx_task, "uart_rx_task", 1024 * 2, NULL, configMAX_PRIORITIES,
NULL);
}

20
main/common.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "esp_log.h"
#define ESP_ERROR_RETRY(action, max_retries) \
{ \
uint8_t retry_count = 0; \
esp_err_t error; \
while (retry_count < max_retries) { \
error = action; \
if (error == ESP_OK) break; \
retry_count++; \
ESP_LOGI("RETRY", "Retrying... (%d/%d)\n", retry_count + 1, \
max_retries); \
} \
if (error != ESP_OK) { \
ESP_LOGE("RETRY", "retry failed. %d", error); \
ESP_ERROR_CHECK(error); \
} \
}

26
main/config_key.h Normal file
View File

@@ -0,0 +1,26 @@
#pragma once
typedef enum e_ui_input_key {
ui_input_key_display_0_brightness = 0,
ui_input_key_display_1_brightness = 1,
ui_input_key_computer_volume = 2,
ui_input_key_display_ambient_lighting_level = 3,
ui_input_key_display_ambient_lighting_mode = 4,
ui_input_key_display_0_mode = 5,
ui_input_key_display_1_mode = 6,
} e_ui_input_key_t;
typedef struct s_ui_input {
e_ui_input_key_t key;
int16_t value;
} s_ui_input_t;
typedef enum e_ui_input_raw_key {
ui_input_raw_key_encoder_0 = 0,
ui_input_raw_key_encoder_1 = 1,
} e_ui_input_raw_key_t;
typedef struct s_ui_input_raw {
e_ui_input_raw_key_t key;
uint8_t value;
} s_ui_input_raw_t;

304
main/desktop.c Normal file
View File

@@ -0,0 +1,304 @@
#include <lwip/netdb.h>
#include <string.h>
#include <sys/param.h>
#include "esp_err.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "gui.c"
#include "light.c"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "nvs_flash.h"
#include "ui_input.c"
#define UDP_PORT 23042
static const char *UDP_SERVER_TAG = "UDP_SERVER";
static bool desktop_connected = false;
static uint64_t last_desktop_ping_at = 0;
static struct sockaddr *desktop_addr = NULL;
static int sock = -1;
static uint8_t tx_buffer[128];
typedef struct desktop_value {
uint8_t value;
uint8_t max;
uint8_t min;
} desktop_value_t;
static desktop_value_t display1_brightness = {
.value = 20,
.max = 100,
.min = 0,
};
static desktop_value_t display2_brightness = {
.value = 20,
.max = 100,
.min = 0,
};
static desktop_value_t computer_volume = {
.value = 20,
.max = 100,
.min = 0,
};
static void udp_server_task(void *pvParameters) {
char rx_buffer[1024];
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
struct sockaddr_in dest_addr;
while (1) {
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(UDP_PORT);
ip_protocol = IPPROTO_IP;
sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(UDP_SERVER_TAG, "Unable to create socket: errno %d. sock: %d",
errno, sock);
break;
}
ESP_LOGI(UDP_SERVER_TAG, "Socket created");
// Set timeout
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout);
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(UDP_SERVER_TAG, "Socket unable to bind: errno %d. sock: %d",
errno, sock);
sock = -1;
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(UDP_SERVER_TAG, "Socket bound, port %d", UDP_PORT);
struct sockaddr source_addr; // Large enough for both IPv4 or IPv6
socklen_t socklen = sizeof(source_addr);
while (1) {
// ESP_LOGI(UDP_SERVER_TAG, "Waiting for data");
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0,
&source_addr, &socklen);
// Error occurred during receiving
if (len < 0) {
if (errno == EAGAIN) {
continue;
}
ESP_LOGE(UDP_SERVER_TAG, "recvfrom failed: errno %d. len: %d", errno,
len);
}
// Data received
else {
// Get the sender's ip address as string
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str,
sizeof(addr_str) - 1);
rx_buffer[len] = 0; // Null-terminate whatever we received and treat
// like a string...
switch (rx_buffer[0]) {
case 1:
last_desktop_ping_at = esp_timer_get_time();
ESP_LOGD(UDP_SERVER_TAG, "Received ping from %s", addr_str);
sendto(sock, rx_buffer, 1, 0, &source_addr, sizeof(source_addr));
desktop_addr = &source_addr;
break;
case 2:
set_display_ambient_light_colors(
((uint16_t)rx_buffer[1] << 8 | (uint16_t)rx_buffer[2]),
(uint8_t *)&(rx_buffer[3]), len - 3);
break;
case 3: // computer brightness changed
if (rx_buffer[1] == 0) {
display1_brightness.value = rx_buffer[2];
} else if (rx_buffer[1] == 1) {
display2_brightness.value = rx_buffer[2];
}
break;
case 4: // computer volume changed
computer_volume.value = rx_buffer[1];
break;
case 5: // color correction changed
ESP_LOGD(UDP_SERVER_TAG, "Color correction changed");
led_strip_set_color_calibration((float)rx_buffer[1] / 255,
(float)rx_buffer[2] / 255,
(float)rx_buffer[3] / 255);
break;
default:
ESP_LOG_BUFFER_HEXDUMP(UDP_SERVER_TAG, rx_buffer, len,
ESP_LOG_INFO);
break;
}
ESP_LOGD(UDP_SERVER_TAG, "Received %d bytes from %s:", len, addr_str);
}
if (sock != -1 && len < 0) {
ESP_LOGE(UDP_SERVER_TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
break;
}
}
}
vTaskDelete(NULL);
}
static uint8_t desktop_change_value(desktop_value_t *target, int8_t delta) {
if (delta > 0) {
if ((target->max - target->value) > delta) {
target->value += delta;
} else {
target->value = target->max;
}
} else {
if ((target->value - target->min) > -delta) {
target->value += delta;
} else {
target->value = target->min;
}
}
return target->value;
}
static uint8_t change_display_brightness(uint8_t display_index, int8_t delta) {
if (desktop_addr == NULL) {
ESP_LOGW(UDP_SERVER_TAG, "No desktop connected");
return 0;
}
switch (display_index) {
case 0: {
uint8_t value = desktop_change_value(&display1_brightness, delta);
tx_buffer[0] = 3;
tx_buffer[1] = 0;
tx_buffer[2] = value;
int err =
sendto(sock, tx_buffer, 3, 0, desktop_addr, sizeof(*desktop_addr));
if (err < 0) {
ESP_LOGE(UDP_SERVER_TAG, "Socket unable to send: errno %d. sock: %d",
errno, sock);
}
gui_change_display_brightness(display_index, value);
return value;
}
case 1: {
uint8_t value = desktop_change_value(&display1_brightness, delta);
tx_buffer[0] = 3;
tx_buffer[1] = 1;
tx_buffer[2] = value;
int err =
sendto(sock, tx_buffer, 3, 0, desktop_addr, sizeof(*desktop_addr));
if (err < 0) {
ESP_LOGE(UDP_SERVER_TAG, "Socket unable to send: errno %d. sock: %d",
errno, sock);
}
gui_change_display_brightness(display_index, value);
return value;
}
default:
ESP_LOGE(UDP_SERVER_TAG, "Invalid display index: %d", display_index);
return 0;
}
}
static uint8_t change_volume(int8_t delta) {
if (desktop_addr == NULL) {
ESP_LOGW(UDP_SERVER_TAG, "No desktop connected");
return 0;
}
uint8_t value = desktop_change_value(&computer_volume, delta);
tx_buffer[0] = 4;
tx_buffer[1] = value;
int err = sendto(sock, tx_buffer, 2, 0, desktop_addr, sizeof(*desktop_addr));
if (err < 0) {
char addr_str[128];
inet_ntoa_r(((struct sockaddr_in *)desktop_addr)->sin_addr, addr_str,
sizeof(addr_str) - 1);
ESP_LOGI(UDP_SERVER_TAG, "addr: %s", addr_str);
ESP_LOGE(UDP_SERVER_TAG, "Socket unable to send: errno %d. sock: %d", errno,
sock);
}
gui_change_volume_level(value);
return value;
}
static void desktop_watch_input_task(void *arg) {
s_ui_input_t input;
for (;;) {
if (xQueueReceive(ui_input_event, &input, portMAX_DELAY)) {
switch (input.key) {
case ui_input_key_display_0_brightness:
change_display_brightness(0, input.value);
break;
case ui_input_key_display_1_brightness:
change_display_brightness(1, input.value);
break;
case ui_input_key_computer_volume:
change_volume(input.value);
break;
default:
break;
}
}
}
}
static void change_desktop_connection_status(bool connected) {
desktop_connected = connected;
if (connected) {
gui_set_server_connected();
} else {
gui_set_server_disconnected();
}
}
static void desktop_online_check_task(void *pvParameters) {
while (1) {
if (esp_timer_get_time() - last_desktop_ping_at > 5000000) { // 2 seconds
change_desktop_connection_status(false);
} else {
change_desktop_connection_status(true);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void udp_server_init(void) {
xTaskCreate(udp_server_task, "udp_server", 4096, (void *)AF_INET, 5, NULL);
xTaskCreate(desktop_online_check_task, "desktop_online_check", 1024, NULL, 10,
NULL);
xTaskCreate(desktop_watch_input_task, "desktop_watch_input", 4096, NULL, 7,
NULL);
}

View File

@@ -1,104 +0,0 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "codetab.h"
#include "driver/i2c.h"
#include "esp_log.h"
#define Brightness 0xCF
#define X_WIDTH 128
#define Y_WIDTH 64
#define I2C_ADDRESS 0x78
#define I2C_MASTER_NUM CONFIG_I2C_NUM
void i2cWriteByte(uint8_t reg, uint8_t data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, I2C_ADDRESS | I2C_MASTER_WRITE, true);
i2c_master_write_byte(cmd, reg, true);
i2c_master_write_byte(cmd, data, true);
i2c_master_stop(cmd);
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
}
void i2cWriteCommand(uint8_t data) { i2cWriteByte(0x00, data); }
void i2cWriteData(uint8_t data) { i2cWriteByte(0x40, data); }
void display_fill(uint8_t bmpData) {
for (int y = 0; y < 8; y++) {
i2cWriteCommand(0xB0 + y);
i2cWriteCommand(0x01);
i2cWriteCommand(0x10);
for (int x = 0; x < X_WIDTH; x++) {
i2cWriteData(bmpData);
}
}
}
void display_fill_rect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1,
uint8_t bmpData) {
for (int y = y0; y < y1; y++) {
i2cWriteCommand(0xB0 + y);
i2cWriteCommand(0x01);
i2cWriteCommand(0x10);
for (int x = x0; x < x1; x++) {
i2cWriteData(bmpData);
}
}
}
void display_set_pos(uint8_t x, uint8_t y) {
i2cWriteCommand(0xB0 + y);
i2cWriteCommand(((x & 0xF0) >> 4) | 0x10);
i2cWriteCommand((x & 0x0F) | 0x01);
}
void display_print8_str(uint8_t x, uint8_t y, char str[]) {
uint8_t c;
for (uint8_t ch, ci = 0; ch = str[ci], ch != '\0'; ci++, x += 8) {
c = ch - 0x20;
display_set_pos(x, y);
for (uint8_t cx = 0; cx < 8; cx++) {
i2cWriteData(F8X16[c * 16 + cx]);
}
display_set_pos(x, y + 1);
for (uint8_t cx = 0; cx < 8; cx++) {
i2cWriteData(F8X16[c * 16 + cx + 8]);
};
}
}
void init_display() {
i2cWriteCommand(0xAE); // display off
i2cWriteCommand(0x00); // set lower column address
i2cWriteCommand(0x10); // set higher column address
i2cWriteCommand(0x40); // set start line address
i2cWriteCommand(0x81); // set contrast control register
i2cWriteCommand(0xCF); // set contrast
i2cWriteCommand(0xA1); // set segment re-map
i2cWriteCommand(0xC8); // set COM output scan direction
i2cWriteCommand(0xA6); // set normal display
i2cWriteCommand(0xA8); // set multiplex ratio(1 to 64)
i2cWriteCommand(0x3F); // 1/64 duty
i2cWriteCommand(0xD3); // set display offset
i2cWriteCommand(0x00); // not offset
i2cWriteCommand(0xD5); // set display clock divide ratio/oscillator frequency
i2cWriteCommand(0x80); // set divide ratio
i2cWriteCommand(0xD9); // set pre-charge period
i2cWriteCommand(0xF1); // set pre-charge voltage
i2cWriteCommand(0xDA); // set com pins hardware configuration
i2cWriteCommand(0x12);
i2cWriteCommand(0xDB); // set vcomh
i2cWriteCommand(0x40); // 0.77*vcc
i2cWriteCommand(0x20); // set memory addressing mode
i2cWriteCommand(0x02); // set page addressing mode
i2cWriteCommand(0x8D); // set charge pump enable/disable
i2cWriteCommand(0x14); // set(0x10) disable
i2cWriteCommand(0xAF); // display on
display_fill(0x00);
display_set_pos(0, 0);
}

2461
main/fonts/app_10.c Normal file

File diff suppressed because it is too large Load Diff

578
main/gui.c Normal file
View File

@@ -0,0 +1,578 @@
#pragma once
#include <stdio.h>
#include "app_icon_8.c"
#include "ch1116.c"
#include "driver/i2c.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "fonts/app_10.c"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "lvgl.h"
static const char *GUI_TAG = "LVGL_GUI";
#define I2C_HOST 0
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (400 * 1000)
#define EXAMPLE_LCD_H_RES CH1116_WIDTH
#define EXAMPLE_LCD_V_RES CH1116_HEIGHT
#define EXAMPLE_LVGL_TICK_PERIOD_MS 2
// EF 9A AA
#define APP_WIFI_WEAK_SYMBOL "\xEF\x9A\xAA"
// EF 9A AB
#define APP_WIFI_FAIR_SYMBOL "\xEF\x9A\xAB"
// EF 87 AB
#define APP_WIFI_GOOD_SYMBOL "\xEF\x87\xAB"
// EE 87 8B
#define APP_CONNECTED_SYMBOL "\xEE\x87\x8B"
// EE 87 8C
#define APP_DISCONNECTED_SYMBOL "\xEE\x87\x8C"
// EE 8A 9E
#define APP_TIMER_SYMBOL "\xEE\x8A\x9E"
// EF 8D 97
#define APP_UP_SYMBOL "\xEF\x8D\x97"
// EF 8D 94
#define APP_DOWN_SYMBOL "\xEF\x8D\x94"
#define GUI_PANEL_TIMEOUT_US 3000000 // 3s
extern void example_lvgl_demo_ui(lv_disp_t *disp);
static void example_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area,
lv_color_t *color_map) {
int offsetx1 = area->x1;
int offsetx2 = area->x2;
int offsety1 = area->y1;
int offsety2 = area->y2;
// copy a buffer's content to a specific area of the display
ch1116_draw_bitmap(offsetx1, offsety1, offsetx2, offsety2, color_map);
lv_disp_flush_ready(drv);
}
static void example_lvgl_set_px_cb(lv_disp_drv_t *disp_drv, uint8_t *buf,
lv_coord_t buf_w, lv_coord_t x, lv_coord_t y,
lv_color_t color, lv_opa_t opa) {
uint16_t byte_index = x + ((y >> 3) * buf_w);
uint8_t bit_index = y & 0x7;
if ((color.full == 0) && (LV_OPA_TRANSP != opa)) {
buf[byte_index] |= (1 << bit_index);
} else {
buf[byte_index] &= ~(1 << bit_index);
}
}
typedef struct value_setting_panel {
lv_obj_t *panel;
lv_obj_t *bar;
lv_obj_t *label;
lv_obj_t *value_label;
int64_t last_updated_at;
} value_setting_panel_t;
static lv_obj_t *wifi_label;
static lv_anim_t wifi_animate;
static lv_obj_t *timer_label;
static lv_anim_t timer_animate;
static lv_obj_t *desktop_label;
static lv_anim_t desktop_animate;
value_setting_panel_t *value_setting_panel = NULL;
static bool panel_locked = false;
static lv_obj_t *scr;
typedef struct gui_network_speed_panel_s {
lv_obj_t *panel;
lv_obj_t *direct_upload_label;
lv_obj_t *direct_download_label;
lv_obj_t *proxy_upload_label;
lv_obj_t *proxy_download_label;
} gui_network_speed_panel_t;
static gui_network_speed_panel_t *gui_network_speed_panel = NULL;
static void example_lvgl_rounder(lv_disp_drv_t *disp_drv, lv_area_t *area) {
area->y1 = area->y1 & (~0x7);
area->y2 = area->y2 | 0x7;
}
static void example_increase_lvgl_tick(void *arg) {
/* Tell LVGL how many milliseconds has elapsed */
lv_tick_inc(EXAMPLE_LVGL_TICK_PERIOD_MS);
}
static void set_value(void *bar, int32_t v) {
lv_bar_set_value(bar, v, LV_ANIM_OFF);
}
static void set_visible(void *obj, int32_t v) {
if (v) {
lv_obj_clear_flag(obj, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
}
}
static void set_obj_blink_animate(lv_obj_t *obj, lv_anim_t *a) {
lv_anim_init(a);
lv_anim_set_exec_cb(a, set_visible);
lv_anim_set_time(a, 100);
lv_anim_set_playback_time(a, 100);
lv_anim_set_playback_delay(a, 200);
lv_anim_set_repeat_delay(a, 200);
lv_anim_set_values(a, 0, 1);
lv_anim_set_var(a, obj);
lv_anim_set_repeat_count(a, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(a);
}
static void change_wifi_icon(void *obj, int32_t v) {
switch (v) {
case 0:
lv_label_set_text(obj, APP_WIFI_WEAK_SYMBOL);
break;
case 1:
lv_label_set_text(obj, APP_WIFI_FAIR_SYMBOL);
break;
case 2:
lv_label_set_text(obj, APP_WIFI_GOOD_SYMBOL);
break;
default:
ESP_LOGW(GUI_TAG, "Unknown wifi strength: %ld", v);
break;
}
}
static void gui_set_wifi_connecting() {
lv_obj_clear_flag(wifi_label, LV_OBJ_FLAG_HIDDEN);
lv_anim_init(&wifi_animate);
lv_anim_set_exec_cb(&wifi_animate, change_wifi_icon);
lv_anim_set_time(&wifi_animate, 300);
lv_anim_set_repeat_delay(&wifi_animate, 300);
lv_anim_set_values(&wifi_animate, 0, 2);
lv_anim_set_var(&wifi_animate, wifi_label);
lv_anim_set_repeat_count(&wifi_animate, LV_ANIM_REPEAT_INFINITE);
lv_anim_start(&wifi_animate);
}
static void gui_set_wifi_connected() {
lv_anim_del(wifi_label, NULL);
lv_obj_clear_flag(wifi_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(wifi_label, APP_WIFI_GOOD_SYMBOL);
vTaskDelay(100 / portTICK_PERIOD_MS);
lv_obj_clear_flag(wifi_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(wifi_label, APP_WIFI_GOOD_SYMBOL);
}
static void gui_set_wifi_disconnected() {
lv_anim_del(&wifi_label, NULL);
vTaskDelay(300 / portTICK_PERIOD_MS);
lv_obj_clear_flag(wifi_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(wifi_label, APP_WIFI_WEAK_SYMBOL);
}
static void gui_set_server_connecting() {
set_obj_blink_animate(desktop_label, &desktop_animate);
}
static void gui_set_server_connected() {
lv_anim_del(desktop_label, NULL);
lv_obj_clear_flag(desktop_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(desktop_label, APP_CONNECTED_SYMBOL);
vTaskDelay(100 / portTICK_PERIOD_MS);
lv_obj_clear_flag(desktop_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(desktop_label, APP_CONNECTED_SYMBOL);
}
static void gui_set_server_disconnected() {
lv_anim_del(desktop_label, NULL);
vTaskDelay(300 / portTICK_PERIOD_MS);
lv_obj_clear_flag(desktop_label, LV_OBJ_FLAG_HIDDEN);
lv_label_set_text(desktop_label, APP_DISCONNECTED_SYMBOL);
}
static void gui_bar_value_update_cb(lv_event_t *e) {
if (value_setting_panel == NULL) {
ESP_LOGW(GUI_TAG, "value_setting_panel is NULL");
return;
}
lv_obj_t *value_label = value_setting_panel->value_label;
lv_obj_draw_part_dsc_t *dsc = lv_event_get_param(e);
if (dsc->part != LV_PART_INDICATOR) return;
lv_obj_t *obj = lv_event_get_target(e);
lv_draw_label_dsc_t label_dsc;
lv_draw_label_dsc_init(&label_dsc);
label_dsc.font = &lv_font_montserrat_10;
char buf[8];
lv_snprintf(buf, sizeof(buf), "%ld", lv_bar_get_value(obj));
lv_point_t txt_size;
lv_txt_get_size(&txt_size, buf, label_dsc.font, label_dsc.letter_space,
label_dsc.line_space, LV_COORD_MAX, label_dsc.flag);
lv_coord_t txt_x;
/*If the indicator is long enough put the text inside on the right*/
if (lv_area_get_width(dsc->draw_area) > txt_size.x + 20) {
txt_x = dsc->draw_area->x2 - 8 - txt_size.x + 1;
lv_obj_set_style_text_color(value_label, lv_color_white(), LV_PART_MAIN);
label_dsc.color = lv_color_white();
}
/*If the indicator is still short put the text out of it on the right*/
else {
txt_x = dsc->draw_area->x2 - 8 + txt_size.x - 1;
lv_obj_set_style_text_color(value_label, lv_color_black(), LV_PART_MAIN);
}
lv_obj_align(value_label, LV_ALIGN_LEFT_MID, txt_x, 0);
lv_label_set_text(value_label, buf);
lv_obj_set_width(value_label, txt_size.x);
}
static void gui_close_value_setting_panel() {
if (value_setting_panel == NULL) return;
lv_obj_del(value_setting_panel->panel);
free(value_setting_panel);
value_setting_panel = NULL;
}
static void gui_check_value_setting_panel_task(void *pvParameters) {
vTaskDelay(GUI_PANEL_TIMEOUT_US / 1000 / portTICK_PERIOD_MS);
for (;;) {
if (!panel_locked &&
esp_timer_get_time() - value_setting_panel->last_updated_at >
GUI_PANEL_TIMEOUT_US) {
gui_close_value_setting_panel();
vTaskDelete(NULL);
}
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
static void gui_create_value_setting_panel() {
if (value_setting_panel != NULL) {
value_setting_panel->last_updated_at = esp_timer_get_time();
return;
}
value_setting_panel =
(value_setting_panel_t *)malloc(sizeof(value_setting_panel_t));
xTaskCreate(gui_check_value_setting_panel_task, "G_CHK_VAL_SET", 1024, NULL,
configMAX_PRIORITIES - 1, NULL);
value_setting_panel->panel = lv_obj_create(scr);
lv_obj_set_size(value_setting_panel->panel, 128, 40);
lv_obj_align(value_setting_panel->panel, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_style_border_width(value_setting_panel->panel, 1, LV_PART_MAIN);
lv_obj_set_style_border_color(value_setting_panel->panel, lv_color_black(),
LV_PART_MAIN);
lv_obj_set_style_radius(value_setting_panel->panel, 5, LV_PART_MAIN);
lv_obj_set_style_pad_all(value_setting_panel->panel, 2, LV_PART_MAIN);
value_setting_panel->label = lv_label_create(value_setting_panel->panel);
lv_obj_align(value_setting_panel->label, LV_ALIGN_BOTTOM_LEFT, 0, 0);
value_setting_panel->bar = lv_bar_create(value_setting_panel->panel);
lv_obj_set_size(value_setting_panel->bar, 120, 12);
lv_obj_align(value_setting_panel->bar, LV_ALIGN_TOP_MID, 0, 0);
lv_bar_set_range(value_setting_panel->bar, 0, 100);
lv_bar_set_value(value_setting_panel->bar, 50, LV_ANIM_ON);
lv_obj_set_style_bg_color(value_setting_panel->bar, lv_color_white(),
LV_PART_MAIN);
lv_obj_set_style_border_color(value_setting_panel->bar, lv_color_black(),
LV_PART_MAIN);
lv_obj_set_style_border_width(value_setting_panel->bar, 1, LV_PART_MAIN);
lv_obj_set_style_radius(value_setting_panel->bar, 5, LV_PART_MAIN);
lv_obj_set_style_pad_hor(value_setting_panel->bar, 0, LV_PART_MAIN);
lv_obj_set_style_pad_ver(value_setting_panel->bar, 2, LV_PART_MAIN);
lv_obj_set_style_bg_color(value_setting_panel->bar, lv_color_black(),
LV_PART_INDICATOR);
lv_obj_add_event_cb(value_setting_panel->bar, gui_bar_value_update_cb,
LV_EVENT_DRAW_PART_END, NULL);
value_setting_panel->value_label = lv_label_create(value_setting_panel->bar);
lv_obj_align(value_setting_panel->value_label, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_text_font(value_setting_panel->value_label,
&lv_font_montserrat_10, LV_PART_MAIN);
}
static void gui_change_strip_level(int32_t target_value) {
panel_locked = true;
gui_create_value_setting_panel();
lv_label_set_text(value_setting_panel->label, "Strip Level");
lv_bar_set_range(value_setting_panel->bar, 0, 255);
lv_bar_set_value(value_setting_panel->bar, target_value, LV_ANIM_ON);
panel_locked = false;
}
static void gui_change_display_brightness(int8_t display_index,
int32_t target_value) {
panel_locked = true;
gui_create_value_setting_panel();
lv_label_set_text_fmt(value_setting_panel->label, "Display#%d Brightness",
display_index);
lv_bar_set_range(value_setting_panel->bar, 0, 100);
lv_bar_set_value(value_setting_panel->bar, target_value, LV_ANIM_ON);
panel_locked = false;
}
static void gui_change_volume_level(int32_t target_value) {
panel_locked = true;
gui_create_value_setting_panel();
lv_label_set_text(value_setting_panel->label, "Volume Level");
lv_bar_set_range(value_setting_panel->bar, 0, 100);
lv_bar_set_value(value_setting_panel->bar, target_value, LV_ANIM_ON);
panel_locked = false;
}
static void gui_network_speed_human_readable(uint64_t *speed, char *str) {
if (*speed > 1024 * 1024 * 1024) {
float v = (float)*speed / 1024.0f / 1024.0f / 1024.0f;
if (v >= 1000) {
sprintf(str, "% 5.0f GB/s", v);
} else if (v >= 100) {
sprintf(str, "% 5.1f GB/s", v);
} else {
sprintf(str, "% 5.2f GB/s", v);
}
} else if (*speed > 1024 * 1024) {
float v = (float)*speed / 1024.0f / 1024.0f;
if (v >= 1000) {
sprintf(str, "% 5.0f MB/s", v);
} else if (v >= 100) {
sprintf(str, "% 5.1f MB/s", v);
} else {
sprintf(str, "% 5.2f MB/s", v);
}
} else if (*speed > 1024) {
float v = (float)*speed / 1024.0f;
if (v >= 1000) {
sprintf(str, "% 5.0f KB/s", v);
} else if (v >= 100) {
sprintf(str, "% 5.1f KB/s", v);
} else {
sprintf(str, "% 5.2f KB/s", v);
}
} else {
sprintf(str, "% 5d B/s", (uint8_t)*speed);
}
}
static void gui_update_network_label_style(lv_obj_t *label, uint64_t *speed) {
if (*speed > 1024 * 1024 * 50 / 8) { // 50 Mbps
lv_obj_set_style_bg_color(label, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_text_color(label, lv_color_white(), LV_PART_MAIN);
} else {
lv_obj_set_style_bg_color(label, lv_color_white(), LV_PART_MAIN);
lv_obj_set_style_text_color(label, lv_color_black(), LV_PART_MAIN);
}
}
static void gui_change_network_speed(uint64_t *direct_upload_speed,
uint64_t *direct_download_speed,
uint64_t *proxy_upload_speed,
uint64_t *proxy_download_speed) {
if (gui_network_speed_panel == NULL) {
return;
}
static char str[32];
strcpy(str, APP_UP_SYMBOL);
gui_network_speed_human_readable(direct_upload_speed, &str[3]);
lv_label_set_text(gui_network_speed_panel->direct_upload_label, str);
gui_network_speed_human_readable(proxy_upload_speed, &str[3]);
lv_label_set_text(gui_network_speed_panel->proxy_upload_label, str);
strcpy(str, APP_DOWN_SYMBOL);
gui_network_speed_human_readable(direct_download_speed, &str[3]);
lv_label_set_text(gui_network_speed_panel->direct_download_label, str);
gui_network_speed_human_readable(proxy_download_speed, &str[3]);
lv_label_set_text(gui_network_speed_panel->proxy_download_label, str);
gui_update_network_label_style(gui_network_speed_panel->direct_upload_label,
direct_upload_speed);
gui_update_network_label_style(gui_network_speed_panel->direct_download_label,
direct_download_speed);
gui_update_network_label_style(gui_network_speed_panel->proxy_upload_label,
proxy_upload_speed);
gui_update_network_label_style(gui_network_speed_panel->proxy_download_label,
proxy_download_speed);
}
static void gui_init_network_speed(lv_obj_t *scr) {
gui_network_speed_panel =
(gui_network_speed_panel_t *)malloc(sizeof(gui_network_speed_panel_t));
gui_network_speed_panel->panel = lv_obj_create(scr);
lv_obj_set_size(gui_network_speed_panel->panel, 128, 40);
lv_obj_align(gui_network_speed_panel->panel, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_obj_set_style_bg_color(gui_network_speed_panel->panel, lv_color_white(),
LV_PART_MAIN);
lv_obj_set_style_border_color(gui_network_speed_panel->panel,
lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_border_width(gui_network_speed_panel->panel, 1,
LV_PART_MAIN);
lv_obj_set_style_radius(gui_network_speed_panel->panel, 5, LV_PART_MAIN);
lv_obj_set_style_pad_hor(gui_network_speed_panel->panel, 1, LV_PART_MAIN);
lv_obj_set_style_pad_ver(gui_network_speed_panel->panel, 1, LV_PART_MAIN);
gui_network_speed_panel->direct_upload_label =
lv_label_create(gui_network_speed_panel->panel);
lv_obj_align(gui_network_speed_panel->direct_upload_label, LV_ALIGN_TOP_LEFT,
0, 0);
lv_obj_set_style_text_font(gui_network_speed_panel->direct_upload_label,
&app_10, LV_PART_MAIN);
lv_label_set_text(gui_network_speed_panel->direct_upload_label,
APP_UP_SYMBOL " 0.00 KB/s");
lv_obj_set_style_bg_opa(gui_network_speed_panel->direct_upload_label,
LV_OPA_COVER, LV_PART_MAIN);
gui_network_speed_panel->direct_download_label =
lv_label_create(gui_network_speed_panel->panel);
lv_obj_align(gui_network_speed_panel->direct_download_label,
LV_ALIGN_BOTTOM_LEFT, 0, 0);
lv_obj_set_style_text_font(gui_network_speed_panel->direct_download_label,
&app_10, LV_PART_MAIN);
lv_label_set_text(gui_network_speed_panel->direct_download_label,
APP_DOWN_SYMBOL " 0.00 KB/s");
lv_obj_set_style_bg_opa(gui_network_speed_panel->direct_download_label,
LV_OPA_COVER, LV_PART_MAIN);
gui_network_speed_panel->proxy_upload_label =
lv_label_create(gui_network_speed_panel->panel);
lv_obj_align(gui_network_speed_panel->proxy_upload_label, LV_ALIGN_TOP_RIGHT,
0, 0);
lv_obj_set_style_text_font(gui_network_speed_panel->proxy_upload_label,
&app_10, LV_PART_MAIN);
lv_label_set_text(gui_network_speed_panel->proxy_upload_label,
APP_UP_SYMBOL " 0.00 KB/s");
lv_obj_set_style_bg_opa(gui_network_speed_panel->proxy_upload_label,
LV_OPA_COVER, LV_PART_MAIN);
gui_network_speed_panel->proxy_download_label =
lv_label_create(gui_network_speed_panel->panel);
lv_obj_align(gui_network_speed_panel->proxy_download_label,
LV_ALIGN_BOTTOM_RIGHT, 0, 0);
lv_obj_set_style_text_font(gui_network_speed_panel->proxy_download_label,
&app_10, LV_PART_MAIN);
lv_label_set_text(gui_network_speed_panel->proxy_download_label,
APP_DOWN_SYMBOL " 0.00 KB/s");
lv_obj_set_style_bg_opa(gui_network_speed_panel->proxy_download_label,
LV_OPA_COVER, LV_PART_MAIN);
}
void gui_status_bar_create(lv_obj_t *scr) {
// wifi icon
wifi_label = lv_label_create(scr);
lv_label_set_long_mode(wifi_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text(wifi_label, APP_WIFI_GOOD_SYMBOL);
lv_obj_set_width(wifi_label, 10);
lv_obj_align(wifi_label, LV_ALIGN_OUT_TOP_LEFT, 0, 0);
lv_obj_set_style_text_color(wifi_label, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_text_font(wifi_label, &app_icon_8, LV_PART_MAIN);
lv_obj_add_flag(wifi_label, LV_OBJ_FLAG_HIDDEN);
// timer icon
timer_label = lv_label_create(scr);
lv_label_set_long_mode(timer_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text(timer_label, APP_TIMER_SYMBOL);
lv_obj_set_width(timer_label, 10);
lv_obj_align(timer_label, LV_ALIGN_OUT_TOP_LEFT, 12, 0);
lv_obj_set_style_text_color(timer_label, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_text_font(timer_label, &app_icon_8, LV_PART_MAIN);
lv_obj_add_flag(timer_label, LV_OBJ_FLAG_HIDDEN);
// desktop icon
desktop_label = lv_label_create(scr);
lv_label_set_long_mode(desktop_label, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text(desktop_label, APP_CONNECTED_SYMBOL);
lv_obj_set_width(desktop_label, 10);
lv_obj_align(desktop_label, LV_ALIGN_OUT_TOP_LEFT, 24, 0);
lv_obj_set_style_text_color(desktop_label, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_text_font(desktop_label, &app_icon_8, LV_PART_MAIN);
lv_obj_add_flag(desktop_label, LV_OBJ_FLAG_HIDDEN);
}
void example_lvgl_demo_ui(lv_disp_t *disp) {
scr = lv_disp_get_scr_act(disp);
lv_obj_t *label = lv_label_create(scr);
lv_label_set_long_mode(label,
LV_LABEL_LONG_SCROLL_CIRCULAR); /* Circular scroll
*/
lv_label_set_text(label, "Hello Ivan Li!");
lv_obj_set_width(label, 120);
lv_obj_align(label, LV_ALIGN_BOTTOM_RIGHT, 0, 0);
gui_status_bar_create(scr);
gui_init_network_speed(scr);
}
static void gui_tick(void *pvParameters) {
while (1) {
// raise the task priority of LVGL and/or reduce the handler period can
// improve the performance
vTaskDelay(pdMS_TO_TICKS(10));
// The task running lv_timer_handler should have lower priority than that
// running `lv_tick_inc`
lv_timer_handler();
}
}
void gui_main(void) {
static lv_disp_draw_buf_t
disp_buf; // contains internal graphic buffer(s) called draw buffer(s)
static lv_disp_drv_t disp_drv; // contains callback functions
ESP_LOGI(GUI_TAG, "Initialize LVGL library");
lv_init();
// alloc draw buffers used by LVGL
// it's recommended to choose the size of the draw buffer(s) to be at least
// 1/10 screen sized
lv_color_t *buf1 = malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t));
assert(buf1);
lv_color_t *buf2 = malloc(EXAMPLE_LCD_H_RES * 20 * sizeof(lv_color_t));
assert(buf2);
// initialize LVGL draw buffers
lv_disp_draw_buf_init(&disp_buf, buf1, buf2, EXAMPLE_LCD_H_RES * 20);
ESP_LOGI(GUI_TAG, "Register display driver to LVGL");
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = EXAMPLE_LCD_H_RES;
disp_drv.ver_res = EXAMPLE_LCD_V_RES;
disp_drv.flush_cb = example_lvgl_flush_cb;
disp_drv.draw_buf = &disp_buf;
disp_drv.rounder_cb = example_lvgl_rounder;
disp_drv.set_px_cb = example_lvgl_set_px_cb;
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
ESP_LOGI(GUI_TAG, "Install LVGL tick timer");
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
const esp_timer_create_args_t lvgl_tick_timer_args = {
.callback = &example_increase_lvgl_tick, .name = "lvgl_tick"};
esp_timer_handle_t lvgl_tick_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer,
EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
ESP_LOGI(GUI_TAG, "Display LVGL Scroll Text");
example_lvgl_demo_ui(disp);
xTaskCreate(gui_tick, "gui_tick", 4096, (void *)NULL, 5, NULL);
}

78
main/i2c.c Normal file
View File

@@ -0,0 +1,78 @@
#pragma once
#include "driver/i2c.h"
#include "esp_log.h"
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_SDA_IO CONFIG_I2C_SDA
#define I2C_MASTER_SCL_IO CONFIG_I2C_SCL
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
#define I2C_MASTER_NUM CONFIG_I2C_NUM
// 0 1 0 0 A2 A1 A0
#define PCA9555_ADDRESS 0x20
#define GX21M15_ADDRESS 0x48
#define APDS_9930_ADDRESS 0x39
#define APDS_9960_ADDRESS 0x39
#define SSD1306_ADDRESS 0x3c
static const char *I2C_TAG = "APP_I2C";
static uint8_t is_temperature_online = 0;
static uint8_t is_pca9555_online = 0;
static uint8_t is_apds_9930_online = 0;
static uint8_t is_apds_9960_online = 0;
static uint8_t is_embedded_display_online = 0;
void init_i2c() {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode,
I2C_MASTER_RX_BUF_DISABLE,
I2C_MASTER_TX_BUF_DISABLE, 0));
i2c_param_config(I2C_MASTER_NUM, &conf);
ESP_LOGI(I2C_TAG, "I2C initialized");
}
uint8_t i2c_check_slave_exists(uint8_t address) {
ESP_LOGI(I2C_TAG, "Checking if slave 0x%2x exists", address);
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, (address << 1) | I2C_MASTER_WRITE, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t ret = i2c_master_cmd_begin(I2C_NUM_0, cmd, 50 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (ret == ESP_OK) {
ESP_LOGW(I2C_TAG, "Slave 0x%2x found", address);
return 1;
} else if (ret == ESP_ERR_TIMEOUT) {
ESP_LOGW(I2C_TAG, "Slave 0x%2x TIMEOUT", address);
return 1;
} else {
ESP_LOGW(I2C_TAG, "Slave 0x%2x not found", address);
return 0;
}
}
void i2c_check_slaves() {
is_temperature_online = i2c_check_slave_exists(GX21M15_ADDRESS);
is_pca9555_online = i2c_check_slave_exists(PCA9555_ADDRESS);
is_apds_9930_online = i2c_check_slave_exists(APDS_9930_ADDRESS);
is_apds_9960_online = i2c_check_slave_exists(APDS_9960_ADDRESS);
is_embedded_display_online = i2c_check_slave_exists(SSD1306_ADDRESS);
}

18
main/idf_component.yml Normal file
View File

@@ -0,0 +1,18 @@
## IDF Component Manager Manifest File
dependencies:
lvgl/lvgl: "^8.3.6~1"
espressif/mdns: "^1.0.9"
## Required IDF version
idf:
version: ">=5.0.1"
# # 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

View File

@@ -0,0 +1,141 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "led_strip_encoder.h"
#include "esp_check.h"
static const char *TAG = "led_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");
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;
// different led strip might have its own timing requirements, following
// parameter is for WS2812
rmt_bytes_encoder_config_t bytes_encoder_config = {
.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
};
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(&copy_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;
}

View File

@@ -0,0 +1,38 @@
/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "driver/rmt_encoder.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Type of led strip encoder configuration
*/
typedef struct {
uint32_t resolution; /*!< Encoder resolution, in Hz */
} 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

View File

@@ -1,24 +1,22 @@
/* RMT example -- RGB LED Strip #pragma once
#include <string.h>
This example code is in the Public Domain (or CC0 licensed, at your option.) #include "driver/rmt_tx.h"
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "driver/rmt.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "led_strip.h" #include "led_strip_encoder/led_strip_encoder.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "sdkconfig.h" #include "sdkconfig.h"
static const char *LIGHT_TAG = "DisplayAmbientLight_Light"; static const char *LIGHT_TAG = "DisplayAmbientLight_Light";
#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define RMT_TX_GPIO 1 #define RMT_TX_GPIO 1
#define STRIP_LED_NUMBER CONFIG_NUMBER_OF_LEDS #define STRIP_LED_NUMBER CONFIG_NUMBER_OF_LEDS
#define EXAMPLE_CHASE_SPEED_MS (10) #define EXAMPLE_CHASE_SPEED_MS (10)
#define RMT_LED_STRIP_RESOLUTION_HZ 10000000
typedef enum light_mode_e { typedef enum light_mode_e {
light_mode_init = 0, light_mode_init = 0,
@@ -27,10 +25,116 @@ typedef enum light_mode_e {
light_mode_mqtt_connected = 3, light_mode_mqtt_connected = 3,
light_mode_desktop_online = 4, light_mode_desktop_online = 4,
light_mode_desktop_sending_colors = 5, light_mode_desktop_sending_colors = 5,
light_mode_off = 6,
} light_mode_t; } light_mode_t;
led_strip_t *light_led_strip; rmt_channel_handle_t led_chan = NULL;
static uint8_t led_strip_pixels[STRIP_LED_NUMBER * 3];
rmt_encoder_handle_t led_encoder = NULL;
rmt_transmit_config_t tx_config = {
.loop_count = 0, // no transfer loop
};
light_mode_t light_mode; light_mode_t light_mode;
float display_ambient_light_brightness = 1;
uint8_t display_ambient_lighting_level = 255;
float led_strip_red_calibration = 1.0;
float led_strip_green_calibration = 1.0;
float led_strip_blue_calibration = 1.0;
void led_strip_fade_in_light_level(void *pvParameter) {
float target = (float)display_ambient_lighting_level / 255.0;
float step_length = (target - display_ambient_light_brightness) / 40.0;
for (int t = 0; t < 40; t++) {
display_ambient_light_brightness += step_length;
vTaskDelay(pdMS_TO_TICKS(10));
}
display_ambient_light_brightness = target;
vTaskDelete(NULL);
}
void led_strip_set_brightness(uint8_t level) {
if (display_ambient_lighting_level == level) {
return;
}
display_ambient_lighting_level = level;
xTaskCreate(led_strip_fade_in_light_level, "LED_STRIP_FADE_IN_LIGHT_LEVEL",
4096, NULL, 1, NULL);
nvs_handle_t nvs_handle;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &nvs_handle);
err = nvs_set_u8(nvs_handle, "brightness", level);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) saving light level!\n",
esp_err_to_name(err));
nvs_close(nvs_handle);
return;
}
err = nvs_commit(nvs_handle);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) saving light level!\n",
esp_err_to_name(err));
}
nvs_close(nvs_handle);
}
void led_strip_set_color_calibration(float red, float green, float blue) {
led_strip_red_calibration = red;
led_strip_green_calibration = green;
led_strip_blue_calibration = blue;
ESP_LOGI(LIGHT_TAG, "Calibration: %f %f %f", red, green, blue);
nvs_handle_t local_nvs_handle;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &local_nvs_handle);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) opening NVS handle!\n",
esp_err_to_name(err));
return;
}
err = nvs_set_u8(local_nvs_handle, "calibration_r", (uint32_t)(red * 255));
if (err != ESP_OK) {
nvs_close(local_nvs_handle);
ESP_LOGW(LIGHT_TAG, "Error (%s) write calibration_r failed!",
esp_err_to_name(err));
return;
}
err = nvs_set_u8(local_nvs_handle, "calibration_g", (uint8_t)(green * 255));
if (err != ESP_OK) {
nvs_close(local_nvs_handle);
ESP_LOGW(LIGHT_TAG, "Error (%s) calibration_g failed!",
esp_err_to_name(err));
return;
}
err = nvs_set_u8(local_nvs_handle, "calibration_b", (uint8_t)(blue * 255));
if (err != ESP_OK) {
nvs_close(local_nvs_handle);
ESP_LOGW(LIGHT_TAG, "Error (%s) calibration_b failed!",
esp_err_to_name(err));
return;
}
err = nvs_commit(local_nvs_handle);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) save led_strip_red_calibration failed!",
esp_err_to_name(err));
}
nvs_close(local_nvs_handle);
}
void light_strip_transmit_task(void *pvParameter) {
while (1) {
ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, led_strip_pixels,
sizeof(led_strip_pixels), &tx_config));
vTaskDelay(pdMS_TO_TICKS(10));
}
}
/** /**
* @brief Simple helper function, converting HSV color space to RGB color * @brief Simple helper function, converting HSV color space to RGB color
@@ -85,67 +189,111 @@ void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r,
} }
} }
void update_desktop_connection_state() { // void update_desktop_connection_state() {
static uint8_t tick = 0; // static uint8_t tick = 0;
bool beat = tick / 10 % 2 ? 1 : 0; // bool beat = tick / 10 % 2 ? 1 : 0;
switch (light_mode) { // switch (light_mode) {
case light_mode_desktop_online: // case light_mode_desktop_online:
if (beat) { // if (beat) {
ESP_ERROR_CHECK( // led_strip_pixels[0] = 0;
light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77)); // led_strip_pixels[1] = 0;
} // led_strip_pixels[2] = 0;
ESP_ERROR_CHECK( // }
light_led_strip->set_pixel(light_led_strip, 1, 77, 77, 77)); // led_strip_pixels[3] = 10
ESP_ERROR_CHECK( // break;
light_led_strip->set_pixel(light_led_strip, 2, 77, 77, 77)); // case light_mode_mqtt_connected:
break; // if (beat) {
case light_mode_mqtt_connected: // ESP_ERROR_CHECK(
if (beat) { // light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10));
ESP_ERROR_CHECK( // ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77)); // light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10));
ESP_ERROR_CHECK( // }
light_led_strip->set_pixel(light_led_strip, 1, 77, 77, 77)); // ESP_ERROR_CHECK(
} // light_led_strip->set_pixel(light_led_strip, 2, 22, 22, 22));
ESP_ERROR_CHECK( // break;
light_led_strip->set_pixel(light_led_strip, 2, 77, 77, 77)); // case light_mode_idle:
break; // if (beat) {
case light_mode_idle: // ESP_ERROR_CHECK(
if (beat) { // light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77));
ESP_ERROR_CHECK( // ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77)); // light_led_strip->set_pixel(light_led_strip, 1, 77, 77, 77));
ESP_ERROR_CHECK( // ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 1, 77, 77, 77)); // light_led_strip->set_pixel(light_led_strip, 2, 77, 77, 77));
ESP_ERROR_CHECK( // }
light_led_strip->set_pixel(light_led_strip, 2, 77, 77, 77)); // break;
} // default:
break; // break;
default: // }
break;
}
tick++; // tick++;
} // }
void light_for_init() { void light_for_init() {
ESP_LOGI(LIGHT_TAG, "light_for_init"); ESP_LOGI(LIGHT_TAG, "light_for_init");
ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100));
uint32_t red = 0, green = 0, blue = 0; memset(led_strip_pixels, 0, sizeof(led_strip_pixels));
int8_t i = 0;
nvs_handle local_nvs_handle;
esp_err_t err = nvs_open("storage", NVS_READWRITE, &local_nvs_handle);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
}
uint8_t r = 255, g = 255, b = 255;
uint8_t brightness = 200;
err = nvs_get_u8(local_nvs_handle, "calibration_r", &r);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_r!",
esp_err_to_name(err));
}
err = nvs_get_u8(local_nvs_handle, "calibration_g", &g);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_g!",
esp_err_to_name(err));
}
err = nvs_get_u8(local_nvs_handle, "calibration_b", &b);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) reading calibration_b!",
esp_err_to_name(err));
}
err = nvs_get_u8(local_nvs_handle, "brightness", &brightness);
if (err != ESP_OK) {
ESP_LOGW(LIGHT_TAG, "Error (%s) reading brightness!", esp_err_to_name(err));
}
nvs_close(local_nvs_handle);
// set brightness
led_strip_set_brightness(brightness);
// play init light
float r_f = (float)r / 255.0;
float g_f = (float)g / 255.0;
float b_f = (float)b / 255.0;
uint8_t init_r, init_g, init_b;
do { do {
for (; i < 100; i++) { for (uint8_t i = 0; i < 200; i++) {
init_r = (uint8_t)(r_f * (float)i);
init_g = (uint8_t)(g_f * (float)i);
init_b = (uint8_t)(b_f * (float)i);
for (int j = 0; j < STRIP_LED_NUMBER; j++) { for (int j = 0; j < STRIP_LED_NUMBER; j++) {
led_strip_hsv2rgb(0, 0, i, &red, &green, &blue); led_strip_pixels[j * 3 + 0] = init_g;
ESP_ERROR_CHECK( led_strip_pixels[j * 3 + 1] = init_r;
light_led_strip->set_pixel(light_led_strip, j, red, green, blue)); led_strip_pixels[j * 3 + 2] = init_b;
} }
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100));
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(20));
} }
vTaskDelay(pdMS_TO_TICKS(100));
} while (light_mode == light_mode_init); } while (light_mode == light_mode_init);
led_strip_set_color_calibration((float)r / 255.0, (float)g / 255.0,
(float)b / 255.0);
} }
void light_for_connecting_wifi() { void light_for_connecting_wifi() {
@@ -154,11 +302,21 @@ void light_for_connecting_wifi() {
int8_t tick_tock = 0; int8_t tick_tock = 0;
do { do {
ESP_ERROR_CHECK( if (tick_tock) {
light_led_strip->set_pixel(light_led_strip, tick_tock, 150, 150, 0)); led_strip_pixels[0] = 150;
ESP_ERROR_CHECK(light_led_strip->set_pixel(light_led_strip, led_strip_pixels[1] = 150;
(tick_tock + 1) % 2, 0, 200, 0)); led_strip_pixels[2] = 0;
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); led_strip_pixels[3] = 200;
led_strip_pixels[4] = 0;
led_strip_pixels[5] = 0;
} else {
led_strip_pixels[0] = 200;
led_strip_pixels[1] = 0;
led_strip_pixels[2] = 0;
led_strip_pixels[3] = 150;
led_strip_pixels[4] = 150;
led_strip_pixels[5] = 0;
}
tick_tock = !tick_tock; tick_tock = !tick_tock;
vTaskDelay(pdMS_TO_TICKS(200)); vTaskDelay(pdMS_TO_TICKS(200));
} while (light_mode == light_mode_connection_wifi); } while (light_mode == light_mode_connection_wifi);
@@ -166,7 +324,10 @@ void light_for_connecting_wifi() {
void light_for_idle() { void light_for_idle() {
ESP_LOGI(LIGHT_TAG, "light_for_idle"); ESP_LOGI(LIGHT_TAG, "light_for_idle");
ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100));
memset(led_strip_pixels, 0, sizeof(led_strip_pixels));
ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, led_strip_pixels,
sizeof(led_strip_pixels), &tx_config));
uint32_t red = 0, green = 0, blue = 0; uint32_t red = 0, green = 0, blue = 0;
uint16_t step_length = 360 / STRIP_LED_NUMBER; uint16_t step_length = 360 / STRIP_LED_NUMBER;
@@ -178,20 +339,23 @@ void light_for_idle() {
for (uint16_t j = 0, hue = offset; j < STRIP_LED_NUMBER; for (uint16_t j = 0, hue = offset; j < STRIP_LED_NUMBER;
j++, hue += step_length) { j++, hue += step_length) {
// Build RGB values // Build RGB values
led_strip_hsv2rgb(hue, 100, 50, &red, &green, &blue); led_strip_hsv2rgb(hue, 50, 30, &red, &green, &blue);
// Write RGB values to strip driver
ESP_ERROR_CHECK( led_strip_pixels[j * 3 + 0] = green * display_ambient_light_brightness *
light_led_strip->set_pixel(light_led_strip, j, red, green, blue)); led_strip_green_calibration;
led_strip_pixels[j * 3 + 1] =
red * display_ambient_light_brightness * led_strip_red_calibration;
led_strip_pixels[j * 3 + 2] =
blue * display_ambient_light_brightness * led_strip_blue_calibration;
} }
update_desktop_connection_state(); // update_desktop_connection_state();
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100));
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
} }
} }
void light_strip_running_task(void *pv_parameters) { void light_strip_running_task(void *pv_parameters) {
while (true) { while (true) {
if (!light_led_strip) { if (!led_chan) {
ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed 2"); ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed 2");
} }
switch (light_mode) { switch (light_mode) {
@@ -215,43 +379,76 @@ void light_strip_running_task(void *pv_parameters) {
} }
void light_init_strip() { void light_init_strip() {
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO, RMT_TX_CHANNEL); rmt_tx_channel_config_t tx_chan_config = {
// set counter clock to 40MHz .clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
config.clk_div = 2; .gpio_num = RMT_TX_GPIO,
.mem_block_symbols =
64, // increase the block size can make the LED less flickering
.resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ,
.trans_queue_depth = 4, // set the number of transactions that can be
// pending in the background
};
ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan));
ESP_ERROR_CHECK(rmt_config(&config)); ESP_LOGI(LIGHT_TAG, "Install led strip encoder");
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
// install ws2812 driver led_strip_encoder_config_t encoder_config = {
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG( .resolution = RMT_LED_STRIP_RESOLUTION_HZ,
STRIP_LED_NUMBER, (led_strip_dev_t)config.channel); };
light_led_strip = led_strip_new_rmt_ws2812(&strip_config); ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&encoder_config, &led_encoder));
if (!light_led_strip) {
ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed"); ESP_LOGI(LIGHT_TAG, "Enable RMT TX channel");
} ESP_ERROR_CHECK(rmt_enable(led_chan));
// Clear LED strip (turn off all LEDs)
ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100)); ESP_LOGI(LIGHT_TAG, "Start LED rainbow chase");
// Show simple rainbow chasing pattern
ESP_LOGI(LIGHT_TAG, "LED Rainbow Chase Start");
light_mode = light_mode_init; light_mode = light_mode_init;
xTaskCreate(light_strip_running_task, "LIGHT_STRIP_RUNNING_TASK", 4096, NULL, xTaskCreate(light_strip_running_task, "LIGHT_STRIP_RUNNING_TASK", 4096, NULL,
1, NULL); 1, NULL);
xTaskCreate(light_strip_transmit_task, "LIGHT_STRIP_TRANSMIT_TASK", 4096,
NULL, 1, NULL);
} }
void light_play_colors(uint16_t len, uint8_t *buffer) { void light_play_colors(uint16_t len, uint8_t *buffer) {
light_mode = light_mode_desktop_sending_colors; // light_mode = light_mode_desktop_sending_colors;
for (uint16_t led_index = 0, buffer_cursor = 0; // uint16_t black_count = 0; // count of black pixels. r/g/b <= 10
led_index < STRIP_LED_NUMBER && buffer_cursor < len;
led_index++, buffer_cursor += 3) { // for (uint16_t led_index = 0, buffer_cursor = 0;
uint8_t r = buffer[buffer_cursor], g = buffer[buffer_cursor + 1], // led_index < STRIP_LED_NUMBER && buffer_cursor < len;
b = buffer[buffer_cursor + 2]; // led_index++, buffer_cursor += 3) {
ESP_ERROR_CHECK( // uint8_t r = (uint8_t)((float)buffer[buffer_cursor] *
light_led_strip->set_pixel(light_led_strip, led_index, r, g, b)); // display_ambient_light_brightness *
} // led_strip_red_calibration),
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100)); // g = (uint8_t)((float)buffer[buffer_cursor + 1] *
// display_ambient_light_brightness *
// led_strip_green_calibration),
// b = (uint8_t)((float)buffer[buffer_cursor + 2] *
// display_ambient_light_brightness *
// led_strip_blue_calibration);
// if (r <= 7 && g <= 7 && b <= 7) {
// black_count++;
// }
// led_strip_pixels[led_index * 3 + 0] = g;
// led_strip_pixels[led_index * 3 + 1] = r;
// led_strip_pixels[led_index * 3 + 2] = b;
// }
// if (black_count > STRIP_LED_NUMBER / 5 * 4) {
// uint8_t r = (uint8_t)((float)50 * display_ambient_light_brightness *
// led_strip_red_calibration),
// g = (uint8_t)((float)40 * display_ambient_light_brightness *
// led_strip_green_calibration),
// b = (uint8_t)((float)20 * display_ambient_light_brightness *
// led_strip_blue_calibration);
// for (uint16_t led_index = 0; led_index < STRIP_LED_NUMBER; led_index++) {
// led_strip_pixels[led_index * 3 + 0] = g;
// led_strip_pixels[led_index * 3 + 1] = r;
// led_strip_pixels[led_index * 3 + 2] = b;
// }
// }
vTaskDelay(pdMS_TO_TICKS(10)); vTaskDelay(pdMS_TO_TICKS(10));
} }
@@ -259,3 +456,53 @@ void light_play(light_mode_t mode) {
ESP_LOGI(LIGHT_TAG, "light_play: %d", mode); ESP_LOGI(LIGHT_TAG, "light_play: %d", mode);
light_mode = mode; light_mode = mode;
} }
void set_display_ambient_light_colors(uint16_t offset, uint8_t *sub_pixels,
uint16_t len) {
// ESP_LOGI(LIGHT_TAG, "set_display_ambient_light_colors: offset: %d\t len:
// %d",
// offset, len);
light_mode = light_mode_desktop_sending_colors;
uint16_t black_count = 0; // count of black pixels. r/g/b <= 7
uint16_t global_end = len + offset * 3;
if (global_end > STRIP_LED_NUMBER * 3) {
global_end = STRIP_LED_NUMBER * 3;
}
for (uint16_t global_index = offset * 3, local_index = 0;
global_index < global_end; global_index += 3, local_index += 3) {
uint8_t r = (uint8_t)((float)sub_pixels[local_index + 0] *
display_ambient_light_brightness *
led_strip_red_calibration),
g = (uint8_t)((float)sub_pixels[local_index + 1] *
display_ambient_light_brightness *
led_strip_green_calibration),
b = (uint8_t)((float)sub_pixels[local_index + 2] *
display_ambient_light_brightness *
led_strip_blue_calibration);
if (r <= 7 && g <= 7 && b <= 7) {
black_count++;
}
led_strip_pixels[global_index + 0] = g;
led_strip_pixels[global_index + 1] = r;
led_strip_pixels[global_index + 2] = b;
}
if (black_count == len / 3) {
uint8_t r = (uint8_t)((float)10 * display_ambient_light_brightness *
led_strip_red_calibration),
g = (uint8_t)((float)7 * display_ambient_light_brightness *
led_strip_green_calibration),
b = (uint8_t)((float)7 * display_ambient_light_brightness *
led_strip_blue_calibration);
for (uint16_t global_index = offset * 3; global_index < global_end;
global_index += 3) {
led_strip_pixels[global_index + 0] = g;
led_strip_pixels[global_index + 1] = r;
led_strip_pixels[global_index + 2] = b;
}
}
}

View File

@@ -1,70 +1,69 @@
#include "ambient_light.c" // #include "apds_9960.c"
#include "driver/i2c.h" #include "app_nvs.c"
#include "ch1116.c"
#include "ci_03t.c"
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "gui.c"
#include "i2c.c"
#include "light.c" #include "light.c"
#include "mqtt.c" // #include "mqtt.c"
// #include "pca9555.c"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "service_discovery.c"
// #include "temperature.c"
#include "desktop.c"
#include "net_gateway_monitor.c"
#include "ui_input.c"
#include "wifi.c" #include "wifi.c"
#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */ static const char *APP_TAG = "DisplayAmbientLight";
#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
#define I2C_MASTER_SDA_IO CONFIG_I2C_SDA
#define I2C_MASTER_SCL_IO CONFIG_I2C_SCL
#define I2C_MASTER_FREQ_HZ 400000 /*!< I2C master clock frequency */
#define I2C_MASTER_NUM CONFIG_I2C_NUM
#include "embedded_display.c"
#include "temperature.c"
void init_i2c() {
int i2c_master_port = I2C_MASTER_NUM;
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = I2C_MASTER_SDA_IO,
.scl_io_num = I2C_MASTER_SCL_IO,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = I2C_MASTER_FREQ_HZ,
};
i2c_param_config(i2c_master_port, &conf);
ESP_LOGI("I2C", "Enabling I2C");
ESP_ERROR_CHECK(i2c_driver_install(i2c_master_port, conf.mode,
I2C_MASTER_RX_BUF_DISABLE,
I2C_MASTER_TX_BUF_DISABLE, 0));
ESP_LOGI("SCR", "I2C initialized successfully");
}
static const char *TAG = "DisplayAmbientLight";
void app_main(void) { void app_main(void) {
app_nvs_init();
light_init_strip(); light_init_strip();
gpio_install_isr_service(0);
init_i2c(); init_i2c();
init_display(); i2c_check_slaves();
display_print8_str(0, 0, "Ambient Light");
ambient_light_init(); ch1116_main();
ambient_light_auto_fetch(); gui_main();
auto_fetch_temperature();
init_ui(); gui_set_wifi_connecting();
xTaskCreate(publish_ui_input, "ui_input_event", 2048, NULL, 10, NULL); gui_set_server_connecting();
vTaskDelay(pdMS_TO_TICKS(10));
// display_print8_str(0, 0, "Ambient Light");
ci_03t_init();
// apds_9960_init();
// apds_9960_auto_fetch();
// auto_fetch_temperature();
pca9555_init();
ui_input_init();
// xTaskCreate(mqtt_publish_ui_input, "mqtt_publish_ui_input", 2048, NULL, 10,
// NULL);
// vTaskDelay(pdMS_TO_TICKS(10));
light_play(light_mode_connection_wifi); light_play(light_mode_connection_wifi);
if (connect_wifi()) { if (connect_wifi()) {
gui_set_wifi_connected();
light_play(light_mode_idle); light_play(light_mode_idle);
} else {
gui_set_wifi_disconnected();
} }
vTaskDelay(pdMS_TO_TICKS(1000)); udp_server_init();
mqtt_app_start(); net_gateway_monitor_init();
if (waiting_for_mqtt_connected()) { service_discovery_init();
light_play(light_mode_mqtt_connected); // vTaskDelay(pdMS_TO_TICKS(1000));
} // mqtt_app_start();
if (waiting_for_desktop_online()) { // if (waiting_for_mqtt_connected()) {
light_play(light_mode_desktop_online); // light_play(light_mode_mqtt_connected);
} // }
while (waiting_and_get_colors()) { // if (waiting_for_desktop_online()) {
light_play_colors(NUMBER_OF_LEDS * 3, mqtt_colors_buffer); // light_play(light_mode_desktop_online);
} // }
// while (waiting_and_get_colors()) {
// light_play_colors(NUMBER_OF_LEDS * 3, mqtt_colors_buffer);
// }
} }

View File

@@ -1,222 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include "esp_bit_defs.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "mqtt_client.h"
#include "ui.c"
#define MQTT_BROKER_URL CONFIG_MQTT_BROKER_URL
#define NUMBER_OF_LEDS CONFIG_NUMBER_OF_LEDS
#define MQTT_FAILED_BIT BIT0
#define MQTT_CONNECTED_BIT BIT1
#define MQTT_DISCONNECTED_BIT BIT2
#define MQTT_DESKTOP_ONLINE_BIT BIT3
#define MQTT_DESKTOP_OFFLINE_BIT BIT4
#define MQTT_DESKTOP_SENDING_BIT BIT5
#define MQTT_COLORS_STAND_BY_BIT BIT6
#define MQTT_BOARD_KEY_PREFIX "display-ambient-light/board/"
#define MQTT_DESKTOP_KEY_PREFIX "display-ambient-light/desktop/"
#define MQTT_ONLINE_SUFFIX "online"
#define MQTT_COLORS_SUFFIX "colors"
#define MQTT_ALL_SUFFIX "#"
#define MQTT_KEY_BOARD_ONLINE MQTT_BOARD_KEY_PREFIX MQTT_ONLINE_SUFFIX
#define MQTT_KEY_DISPLAY_BRIGHTNESS_INPUT MQTT_BOARD_KEY_PREFIX "brightness"
#define MQTT_KEY_DESKTOP_ONLINE MQTT_DESKTOP_KEY_PREFIX MQTT_ONLINE_SUFFIX
#define MQTT_KEY_DESKTOP_COLORS MQTT_DESKTOP_KEY_PREFIX MQTT_COLORS_SUFFIX
#define MQTT_KEY_DESKTOP_ALL MQTT_DESKTOP_KEY_PREFIX MQTT_ALL_SUFFIX
static const char *MQTT_TAG = "DisplayAmbientLight_MQTT";
static EventGroupHandle_t s_mqtt_event_group;
static esp_mqtt_client_handle_t client = NULL;
typedef struct colors {
uint8_t *buffer;
uint8_t number;
} s_colors_t;
static uint8_t *mqtt_colors_buffer;
static void log_error_if_nonzero(const char *message, int error_code) {
if (error_code != 0) {
ESP_LOGE(MQTT_TAG, "Last error %s: 0x%x", message, error_code);
}
}
static void mqtt_event_handler(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
ESP_LOGD(MQTT_TAG, "Event dispatched from event loop base=%s, event_id=%d",
base, event_id);
esp_mqtt_event_handle_t event = event_data;
client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_CONNECTED");
xEventGroupSetBits(s_mqtt_event_group, MQTT_CONNECTED_BIT);
msg_id = esp_mqtt_client_publish(client, MQTT_KEY_BOARD_ONLINE, "ONLINE",
0, 1, 0);
ESP_LOGI(MQTT_TAG, "sent publish successful, msg_id=%d", msg_id);
esp_mqtt_client_subscribe(client, MQTT_KEY_DESKTOP_ALL, 1);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DISCONNECTED");
xEventGroupSetBits(s_mqtt_event_group, MQTT_DISCONNECTED_BIT);
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
// ESP_LOGI(MQTT_TAG, "MQTT_EVENT_DATA");
if (strncmp(event->topic, MQTT_KEY_DESKTOP_ONLINE, event->topic_len) ==
0) {
xEventGroupSetBits(s_mqtt_event_group, MQTT_DESKTOP_ONLINE_BIT);
} else if (strncmp(event->topic, MQTT_KEY_DESKTOP_COLORS,
event->topic_len) == 0) {
// printf("LEN=%d, DATA=%.*s\r\n", event->data_len, event->data_len,
// event->data);
memcpy(mqtt_colors_buffer, event->data,
MIN(event->data_len, NUMBER_OF_LEDS * 3));
xEventGroupSetBits(s_mqtt_event_group,
MQTT_DESKTOP_SENDING_BIT | MQTT_COLORS_STAND_BY_BIT);
} else {
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(MQTT_TAG, "MQTT_EVENT_ERROR");
xEventGroupSetBits(s_mqtt_event_group, MQTT_FAILED_BIT);
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls",
event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack",
event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno",
event->error_handle->esp_transport_sock_errno);
ESP_LOGI(MQTT_TAG, "Last errno string (%s)",
strerror(event->error_handle->esp_transport_sock_errno));
}
break;
default:
ESP_LOGI(MQTT_TAG, "Other event id:%d", event->event_id);
break;
}
}
static void mqtt_app_start() {
mqtt_colors_buffer = (uint8_t *)malloc(sizeof(uint8_t) * NUMBER_OF_LEDS * 3);
s_mqtt_event_group = xEventGroupCreate();
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = MQTT_BROKER_URL,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler,
client);
esp_mqtt_client_start(client);
}
static bool waiting_for_mqtt_connected() {
EventBits_t bits = xEventGroupWaitBits(s_mqtt_event_group,
MQTT_CONNECTED_BIT | MQTT_FAILED_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & MQTT_CONNECTED_BIT) {
return 1;
} else if (bits & MQTT_FAILED_BIT) {
ESP_LOGE(MQTT_TAG, "BBB Failed to connect to MQTT.");
return 0;
} else {
ESP_LOGE(MQTT_TAG, "UNEXPECTED EVENT");
return 0;
}
}
static bool waiting_for_desktop_online() {
EventBits_t bits = xEventGroupWaitBits(
s_mqtt_event_group, MQTT_DESKTOP_ONLINE_BIT | MQTT_DESKTOP_SENDING_BIT,
pdFALSE, pdFALSE, portMAX_DELAY);
if (bits & MQTT_DESKTOP_ONLINE_BIT) {
return 1;
} else if (bits & MQTT_DESKTOP_SENDING_BIT) {
return 1;
} else {
ESP_LOGE(MQTT_TAG, "UNEXPECTED EVENT");
return 0;
}
}
static bool waiting_for_desktop_sending_colors() {
EventBits_t bits =
xEventGroupWaitBits(s_mqtt_event_group, MQTT_DESKTOP_SENDING_BIT, pdFALSE,
pdFALSE, portMAX_DELAY);
ESP_LOGE(MQTT_TAG, "MQTT_DESKTOP_SENDING_BIT");
if (bits & MQTT_DESKTOP_SENDING_BIT) {
return 1;
} else {
ESP_LOGE(MQTT_TAG, "UNEXPECTED EVENT");
return 0;
}
}
static bool waiting_and_get_colors() {
EventBits_t bits =
xEventGroupWaitBits(s_mqtt_event_group, MQTT_COLORS_STAND_BY_BIT, pdFALSE,
pdFALSE, portMAX_DELAY);
if (bits & MQTT_COLORS_STAND_BY_BIT) {
xEventGroupClearBits(s_mqtt_event_group, MQTT_COLORS_STAND_BY_BIT);
return 1;
} else {
ESP_LOGE(MQTT_TAG, "UNEXPECTED EVENT");
return 0;
}
}
static void publish_ui_input(void *arg) {
s_ui_input_t input;
for (;;) {
if (xQueueReceive(ui_input_event, &input, portMAX_DELAY)) {
switch (input.key) {
case UI_INPUT_DISPLAY_0_BRIGHTNESS:
case UI_INPUT_DISPLAY_1_BRIGHTNESS: {
cJSON *publish_json_root, *brightness;
publish_json_root = cJSON_CreateObject();
cJSON_AddNumberToObject(
publish_json_root, "display_index",
UI_INPUT_DISPLAY_0_BRIGHTNESS == input.key ? 0 : 1);
cJSON_AddItemToObject(publish_json_root, "brightness",
brightness = cJSON_CreateObject());
cJSON_AddNumberToObject(brightness, "Absolute", input.value);
char *publish_str = cJSON_Print(publish_json_root);
cJSON_Delete(publish_json_root);
esp_mqtt_client_publish(client, MQTT_KEY_DISPLAY_BRIGHTNESS_INPUT,
publish_str, 0, 1, 0);
} break;
default:
break;
}
}
}
}

147
main/net_gateway_monitor.c Normal file
View File

@@ -0,0 +1,147 @@
#include <lwip/netdb.h>
#include <string.h>
#include <sys/param.h>
#include "esp_err.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "gui.c"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#define NET_GATEWAY_MONITOR_UDP_PORT 23043
#define NET_GATEWAY_MONITOR_SERVER_ADDR 0xC0A81F05 // 192.168.31.5
#define NET_GATEWAY_MONITOR_SERVER_PORT 17890
static const char *NET_GATEWAY_MONITOR_TAG = "NET_GATE_MONITOR";
static int net_gateway_monitor_socket = -1;
static void net_gateway_monitor_request_task(void *pvParameters) {
int sock = net_gateway_monitor_socket;
struct sockaddr_in server_addr;
server_addr.sin_addr.s_addr = htonl(NET_GATEWAY_MONITOR_SERVER_ADDR);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(NET_GATEWAY_MONITOR_SERVER_PORT);
while (sock == net_gateway_monitor_socket) {
int err = sendto(net_gateway_monitor_socket, "ping", 4, 0, &server_addr,
sizeof(server_addr));
if (err < 0) {
ESP_LOGE(NET_GATEWAY_MONITOR_TAG,
"Error occurred during sending: errno %d. sock: %d", errno,
net_gateway_monitor_socket);
}
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
vTaskDelete(NULL);
}
static void net_gateway_monitor_run_task(void *pvParameters) {
char rx_buffer[1024];
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
struct sockaddr_in dest_addr;
struct sockaddr_in server_addr;
while (1) {
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(NET_GATEWAY_MONITOR_UDP_PORT);
ip_protocol = IPPROTO_IP;
server_addr.sin_addr.s_addr = htonl(NET_GATEWAY_MONITOR_SERVER_ADDR);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(NET_GATEWAY_MONITOR_SERVER_PORT);
net_gateway_monitor_socket = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (net_gateway_monitor_socket < 0) {
ESP_LOGE(NET_GATEWAY_MONITOR_TAG,
"Unable to create socket: errno %d. sock: %d", errno,
net_gateway_monitor_socket);
break;
}
ESP_LOGI(NET_GATEWAY_MONITOR_TAG, "Socket created");
// Set timeout
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
setsockopt(net_gateway_monitor_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout,
sizeof timeout);
int err = bind(net_gateway_monitor_socket, (struct sockaddr *)&dest_addr,
sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(NET_GATEWAY_MONITOR_TAG,
"Socket unable to bind: errno %d. sock: %d", errno,
net_gateway_monitor_socket);
net_gateway_monitor_socket = -1;
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
// err = connect(net_gateway_monitor_socket, (struct sockaddr
// *)&server_addr,
// sizeof(server_addr));
if (err < 0) {
ESP_LOGE(NET_GATEWAY_MONITOR_TAG,
"Socket unable to connect: errno %d. sock: %d", errno,
net_gateway_monitor_socket);
net_gateway_monitor_socket = -1;
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue;
}
ESP_LOGI(NET_GATEWAY_MONITOR_TAG, "Socket bound, port %d",
NET_GATEWAY_MONITOR_UDP_PORT);
xTaskCreate(net_gateway_monitor_request_task, "net_gateway_monitor_request",
1024, NULL, 5, NULL);
while (1) {
uint8_t from_len = sizeof(server_addr);
// ESP_LOGI(NET_GATEWAY_MONITOR_TAG, "Waiting for data");
int len =
recv(net_gateway_monitor_socket, rx_buffer, sizeof(rx_buffer) - 1, 0);
// Error occurred during receiving
if (len < 0) {
if (errno == EAGAIN) {
continue;
}
ESP_LOGE(NET_GATEWAY_MONITOR_TAG, "recvfrom failed: errno %d. len: %d",
errno, len);
}
// Data received
else {
// Get the sender's ip address as string
inet_ntoa_r(server_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
rx_buffer[len] = 0; // Null-terminate whatever we received and treat
// like a string...
gui_change_network_speed(
(uint64_t *)&rx_buffer[0], (uint64_t *)&rx_buffer[8],
(uint64_t *)&rx_buffer[16], (uint64_t *)&rx_buffer[24]);
}
if (net_gateway_monitor_socket != -1 && len < 0) {
ESP_LOGE(NET_GATEWAY_MONITOR_TAG,
"Shutting down socket and restarting...");
shutdown(net_gateway_monitor_socket, 0);
close(net_gateway_monitor_socket);
break;
}
}
}
vTaskDelete(NULL);
}
static void net_gateway_monitor_init() {
ESP_LOGI(NET_GATEWAY_MONITOR_TAG, "Start net gateway monitor");
xTaskCreate(net_gateway_monitor_run_task, "net_gateway_monitor_run", 4096,
(void *)AF_INET, 5, NULL);
}

107
main/pca9555.c Normal file
View File

@@ -0,0 +1,107 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "driver/i2c.h"
#include "esp_log.h"
#include "i2c.c"
#define PCA_9555_TAG "PCA9555"
// Register of command byte
#define PCA95555_CMD_INPUT_PORT_0 0x00
#define PCA95555_CMD_INPUT_PORT_1 0x01
#define PCA95555_CMD_OUTPUT_PORT_0 0x02
#define PCA95555_CMD_OUTPUT_PORT_1 0x03
#define PCA95555_CMD_POLARITY_INVERSION_PORT_0 0x04
#define PCA95555_CMD_POLARITY_INVERSION_PORT_1 0x05
#define PCA95555_CMD_CONFIGURATION_PORT_0 0x06
#define PCA95555_CMD_CONFIGURATION_PORT_1 0x07
esp_err_t pca9555_write_config(uint8_t cmd_byte, uint8_t data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9555_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_byte, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(PCA_9555_TAG, "write config failed. %d", error);
}
return error;
}
esp_err_t pca9555_write_output(uint8_t cmd_byte, uint8_t data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9555_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_byte, ACK_CHECK_EN);
i2c_master_write_byte(cmd, data, ACK_CHECK_EN);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(PCA_9555_TAG, "write output failed. %d", error);
}
return error;
}
esp_err_t pca9555_read_one_input(uint8_t cmd_byte, uint8_t* data) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9555_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN);
i2c_master_write_byte(cmd, cmd_byte, ACK_CHECK_EN);
i2c_master_start(cmd);
i2c_master_write_byte(cmd, PCA9555_ADDRESS << 1 | I2C_MASTER_READ,
ACK_CHECK_EN);
i2c_master_read_byte(cmd, data, NACK_VAL);
i2c_master_stop(cmd);
esp_err_t error =
i2c_master_cmd_begin(I2C_MASTER_NUM, cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(cmd);
if (error != ESP_OK) {
ESP_LOGW(PCA_9555_TAG, "read input failed. %d", error);
}
return error;
}
void pca9555_fetch(void* arg) {
ESP_LOGI(PCA_9555_TAG, "fetching");
esp_err_t error;
uint8_t buff;
char disp_str[17];
for (;;) {
error = pca9555_read_one_input(PCA95555_CMD_INPUT_PORT_1, &buff);
if (error != ESP_OK) {
ESP_LOGW(PCA_9555_TAG, "read failed. %x", error);
} else {
ESP_LOGD(PCA_9555_TAG, "IO0: 0x%2x", buff);
}
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void pca9555_auto_fetch() {
if (is_apds_9930_online == 0) {
return;
}
xTaskCreate(pca9555_fetch, "pac05555-fetching", 2048, NULL, 10, NULL);
}
void pca9555_init() {
if (is_pca9555_online == 0) {
ESP_LOGE(PCA_9555_TAG, "salve offline");
return;
}
ESP_LOGI(PCA_9555_TAG, "Initializing PCA9555");
esp_log_level_set(PCA_9555_TAG, ESP_LOG_DEBUG);
pca9555_write_config(PCA95555_CMD_CONFIGURATION_PORT_1, 0x12);
}

28
main/service_discovery.c Normal file
View File

@@ -0,0 +1,28 @@
#pragma once
#include "esp_err.h"
#include "esp_log.h"
#include "mdns.h"
static const char *SERVICE_DISCOVERY_TAG = "SERVICE_DISCOVERY_TAG";
static void service_discovery_init() { // 初始化 mDNS 服务
esp_err_t err = mdns_init();
if (err) {
ESP_LOGE(SERVICE_DISCOVERY_TAG, "MDNS Init failed: %d\n", err);
return;
}
// 设置 hostname
mdns_hostname_set("ambient-light-board");
// 设置默认实例
mdns_instance_name_set("ivan");
// 添加服务
mdns_service_add(NULL, "_ambient_light", "_udp", 23042, NULL, 0);
mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0);
// 注意:必须先添加服务,然后才能设置其属性
// web 服务器使用自定义的实例名
mdns_service_instance_name_set("_ambientlight_web", "_tcp",
"Ambient Light Board Web Server");
}

View File

@@ -4,37 +4,35 @@
#include "driver/i2c.h" #include "driver/i2c.h"
#include "embedded_display.c" #include "embedded_display.c"
#include "esp_log.h" #include "esp_log.h"
#include "i2c.c"
#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ #define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/
#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ #define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */
#define ACK_VAL 0x0 /*!< I2C ack value */ #define ACK_VAL 0x0 /*!< I2C ack value */
#define NACK_VAL 0x1 /*!< I2C nack value */ #define NACK_VAL 0x1 /*!< I2C nack value */
#define I2C_MASTER_NUM CONFIG_I2C_NUM
#define GX21M15_ADDRESS 0x90
#define GX21M15_TEMP_POINTER_VALUE 0b00 // 温度寄存器 #define GX21M15_TEMP_POINTER_VALUE 0b00 // 温度寄存器
#define GX21M15_CONF_POINTER_VALUE 0b01 // 配置寄存器 #define GX21M15_CONF_POINTER_VALUE 0b01 // 配置寄存器
#define GX21M15_THYST_POINTER_VALUE 0b10 // 回滞寄存器 #define GX21M15_THYST_POINTER_VALUE 0b10 // 回滞寄存器
#define GX21M15_TOS_POINTER_VALUE 0b11 // 过温关断寄存器 #define GX21M15_TOS_POINTER_VALUE 0b11 // 过温关断寄存器
#define DEFAULT_TEMPERATURE -999 // 过温关断寄存器 #define DEFAULT_TEMPERATURE -999 // 过温关断寄存器
#define TEMPERATURE_TAG "temperature" #define TEMPERATURE_TAG "temperature"
void fetch_temperature(void* arg) { void fetch_temperature(void* arg) {
esp_err_t error; esp_err_t error;
float temperature = DEFAULT_TEMPERATURE; float temperature = DEFAULT_TEMPERATURE;
char temperature_str[10]; char temperature_str[20];
uint8_t temperature_buffer[] = {0, 0}; uint8_t temperature_buffer[] = {0, 0};
display_fill_rect(0, 0, 128, 2, 0x00); display_fill_rect(0, 0, 128, 2, 0x00);
for (;;) { for (;;) {
i2c_cmd_handle_t cmd = i2c_cmd_link_create(); i2c_cmd_handle_t cmd = i2c_cmd_link_create();
i2c_master_start(cmd); i2c_master_start(cmd);
i2c_master_write_byte(cmd, GX21M15_ADDRESS | I2C_MASTER_WRITE, i2c_master_write_byte(cmd, GX21M15_ADDRESS << 1 | I2C_MASTER_WRITE,
ACK_CHECK_EN); ACK_CHECK_EN);
i2c_master_write_byte(cmd, 0, ACK_CHECK_EN); i2c_master_write_byte(cmd, 0, ACK_CHECK_EN);
i2c_master_start(cmd); i2c_master_start(cmd);
i2c_master_write_byte(cmd, GX21M15_ADDRESS | I2C_MASTER_READ, ACK_VAL); i2c_master_write_byte(cmd, GX21M15_ADDRESS << 1 | I2C_MASTER_READ, ACK_VAL);
i2c_master_read_byte(cmd, &(temperature_buffer[0]), ACK_VAL); i2c_master_read_byte(cmd, &(temperature_buffer[0]), ACK_VAL);
i2c_master_read_byte(cmd, &(temperature_buffer[1]), NACK_VAL); i2c_master_read_byte(cmd, &(temperature_buffer[1]), NACK_VAL);
i2c_master_stop(cmd); i2c_master_stop(cmd);
@@ -64,11 +62,16 @@ void fetch_temperature(void* arg) {
} }
display_print8_str(8, 0, temperature_str); display_print8_str(8, 0, temperature_str);
} }
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(2000));
} }
display_fill_rect(0, 0, 128, 2, 0x00); display_fill_rect(0, 0, 128, 2, 0x00);
} }
void auto_fetch_temperature() { void auto_fetch_temperature() {
xTaskCreate(fetch_temperature, "ui_input_event", 2048, NULL, 10, NULL); if (is_temperature_online == 0) {
ESP_LOGW(TEMPERATURE_TAG, "temperature is offline");
return;
}
ESP_LOGI(TEMPERATURE_TAG, "auto_fetch_temperature");
xTaskCreate(fetch_temperature, "temperature", 2048, NULL, 10, NULL);
} }

156
main/ui.c
View File

@@ -1,156 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#define GPIO_OUTPUT_IO_0 5
#define GPIO_OUTPUT_IO_1 6
#define GPIO_OUTPUT_PIN_SEL \
((1ULL << GPIO_OUTPUT_IO_0) | (1ULL << GPIO_OUTPUT_IO_1))
#define ENCODER_0_CLK_PIN CONFIG_ENCODER_0_CLK_PIN
#define ENCODER_0_DT_PIN CONFIG_ENCODER_0_DT_PIN
#define ENCODER_0_CLK_PIN_MASK 1ULL << ENCODER_0_CLK_PIN
#define ENCODER_0_DT_PIN_MASK 1ULL << ENCODER_0_DT_PIN
#define ENCODER_1_CLK_PIN CONFIG_ENCODER_1_CLK_PIN
#define ENCODER_1_DT_PIN CONFIG_ENCODER_1_DT_PIN
#define ENCODER_1_CLK_PIN_MASK 1ULL << ENCODER_1_CLK_PIN
#define ENCODER_1_DT_PIN_MASK 1ULL << ENCODER_1_DT_PIN
#define ESP_INTR_FLAG_DEFAULT 0
#define NOT_ROTATING -1
#define CLOCKWISE 1
#define COUNTER_CLOCKWISE 0
#define UI_INPUT_DISPLAY_0_BRIGHTNESS 1
#define UI_INPUT_DISPLAY_1_BRIGHTNESS 2
typedef struct s_ui_input {
uint8_t key;
uint16_t value;
} s_ui_input_t;
static xQueueHandle ui_input_event = NULL;
static int8_t rising_edge_rotation_direction = NOT_ROTATING;
static int8_t falling_edge_rotation_direction = NOT_ROTATING;
static uint8_t display_0_brightness = 20;
static uint8_t display_1_brightness = 20;
static s_ui_input_t current_ui_input = {};
uint8_t input_key;
uint8_t clk_pin = ENCODER_0_CLK_PIN;
uint8_t dt_level;
uint8_t dt_pin = ENCODER_0_DT_PIN;
uint8_t* value = &display_0_brightness;
static void IRAM_ATTR gpio_isr_handler(void* arg) {
uint8_t input_key = (uint8_t)arg;
switch (input_key) {
case UI_INPUT_DISPLAY_0_BRIGHTNESS:
clk_pin = ENCODER_0_CLK_PIN;
dt_pin = ENCODER_0_DT_PIN;
value = &display_0_brightness;
break;
case UI_INPUT_DISPLAY_1_BRIGHTNESS:
clk_pin = ENCODER_1_CLK_PIN;
dt_pin = ENCODER_1_DT_PIN;
value = &display_1_brightness;
break;
default:
break;
}
dt_level = gpio_get_level(dt_pin);
if (gpio_get_level(clk_pin)) {
rising_edge_rotation_direction = dt_level == 0;
if (falling_edge_rotation_direction == rising_edge_rotation_direction) {
if (rising_edge_rotation_direction) {
gpio_set_level(GPIO_OUTPUT_IO_0, 1);
gpio_set_level(GPIO_OUTPUT_IO_1, 0);
*value += 1;
} else {
gpio_set_level(GPIO_OUTPUT_IO_0, 0);
gpio_set_level(GPIO_OUTPUT_IO_1, 1);
*value -= 1;
}
falling_edge_rotation_direction = NOT_ROTATING;
current_ui_input.key = input_key;
current_ui_input.value = *value;
xQueueSendFromISR(ui_input_event, &current_ui_input, NULL);
} else {
if (falling_edge_rotation_direction != NOT_ROTATING) {
rising_edge_rotation_direction = NOT_ROTATING;
}
}
} else {
falling_edge_rotation_direction = dt_level;
// printf("F %d\t", falling_edge_rotation_direction);
if (rising_edge_rotation_direction == falling_edge_rotation_direction) {
if (falling_edge_rotation_direction) {
gpio_set_level(GPIO_OUTPUT_IO_0, 1);
gpio_set_level(GPIO_OUTPUT_IO_1, 0);
*value += 1;
} else {
gpio_set_level(GPIO_OUTPUT_IO_0, 0);
gpio_set_level(GPIO_OUTPUT_IO_1, 1);
*value -= 1;
}
rising_edge_rotation_direction = NOT_ROTATING;
current_ui_input.key = input_key;
current_ui_input.value = *value;
xQueueSendFromISR(ui_input_event, &current_ui_input, NULL);
} else {
if (rising_edge_rotation_direction != NOT_ROTATING) {
falling_edge_rotation_direction = NOT_ROTATING;
}
}
}
}
void init_ui(void) {
// zero-initialize the config structure.
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 = GPIO_OUTPUT_PIN_SEL;
// 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
gpio_config(&io_conf);
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
// interrupt of rising edge
io_conf.intr_type = GPIO_INTR_ANYEDGE;
io_conf.pin_bit_mask = ENCODER_0_CLK_PIN_MASK | ENCODER_1_CLK_PIN_MASK;
gpio_config(&io_conf);
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.pin_bit_mask = ENCODER_0_DT_PIN_MASK | ENCODER_1_DT_PIN_MASK;
gpio_config(&io_conf);
// start encoder task
ui_input_event = xQueueCreate(10, sizeof(s_ui_input_t));
// install gpio isr service
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(ENCODER_0_CLK_PIN, gpio_isr_handler,
(void*)UI_INPUT_DISPLAY_0_BRIGHTNESS);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(ENCODER_1_CLK_PIN, gpio_isr_handler,
(void*)UI_INPUT_DISPLAY_1_BRIGHTNESS);
}

234
main/ui_input.c Normal file
View File

@@ -0,0 +1,234 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config_key.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "gui.c"
#include "light.c"
#include "pca9555.c"
#define ENCODER_0_CLK_PORT_IO CONFIG_ENCODER_0_CLK_PORT_IO
#define ENCODER_0_DT_PORT_IO CONFIG_ENCODER_0_DT_PORT_IO
#define ENCODER_0_SW_PORT_IO CONFIG_ENCODER_0_SW_PORT_IO
#define ENCODER_1_CLK_PORT_IO CONFIG_ENCODER_1_CLK_PORT_IO
#define ENCODER_1_DT_PORT_IO CONFIG_ENCODER_1_DT_PORT_IO
#define ENCODER_1_SW_PORT_IO CONFIG_ENCODER_1_SW_PORT_IO
#define ENCODER_INT_GPIO GPIO_NUM_3
#define ESP_INTR_FLAG_DEFAULT 0
#define RISING_EDGE_NOT_ROTATING 0b00000000
#define RISING_EDGE_CLOCKWISE 0b10000000
#define RISING_EDGE_COUNTER_CLOCKWISE 0b11000000
#define RISING_EDGE_ROTATING_MASK 0b11000000
#define FALLING_EDGE_NOT_ROTATING 0b00000000
#define FALLING_EDGE_CLOCKWISE 0b00100000
#define FALLING_EDGE_COUNTER_CLOCKWISE 0b00110000
#define FALLING_EDGE_ROTATING_MASK 0b00110000
static const char *UI_INPUT_TAG = "UiInput";
static QueueHandle_t ui_input_event = NULL;
static QueueHandle_t ui_input_raw_event = NULL;
typedef struct encoder_state {
e_ui_input_raw_key_t key;
uint8_t bits; // rising_edge_rotation_direction,
// falling_edge_rotation_direction, dt, clk, sw
uint8_t value; // dt, clk, sw
} encoder_state_t;
static encoder_state_t encoder_0_state = {.key = ui_input_raw_key_encoder_0,
.bits = 0};
static encoder_state_t encoder_1_state = {.key = ui_input_raw_key_encoder_1,
.bits = 0};
uint8_t level_byte;
int8_t delta = 0;
int64_t ec0_last_time = 0;
int64_t ec1_last_time = 0;
int64_t ec0_interval = 0;
int64_t ec1_interval = 0;
static void IRAM_ATTR gpio_isr_handler(void *arg) {
xQueueSendFromISR(ui_input_raw_event, NULL, NULL);
}
static void ui_input_update_embedded_display(void *arg) {
s_ui_input_t input;
char changing_str[20] = "NC";
for (;;) {
if (xQueueReceive(ui_input_event, &input, portMAX_DELAY)) {
switch (input.key) {
case ui_input_key_display_0_brightness:
sprintf(changing_str, "Dis0: % 3d", input.value);
break;
case ui_input_key_display_1_brightness:
sprintf(changing_str, "Dis1: % 3d", input.value);
break;
case ui_input_key_computer_volume:
sprintf(changing_str, "CVol: % 3d", input.value);
break;
case ui_input_key_display_ambient_lighting_level:
break;
case ui_input_key_display_ambient_lighting_mode:
sprintf(changing_str, "ALMd: % 3d", input.value);
break;
case ui_input_key_display_0_mode:
sprintf(changing_str, "Dis0M: % 2d", input.value);
break;
case ui_input_key_display_1_mode:
sprintf(changing_str, "Dis1M: % 2d", input.value);
break;
default:
strcpy(changing_str, "NC");
break;
}
ESP_LOGI(UI_INPUT_TAG, "%s", changing_str);
}
}
}
static void encoder_value_change(encoder_state_t *state) {
if ((state->value >> 1 & 1) == (state->bits >> 1 & 1)) {
return;
}
state->bits = (state->bits & 0b11111000) | (state->value & 0b111);
if (state->value >> 1 & 1) {
state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) | 0x80 |
~(state->value >> 2 & 1) << 6;
if (((state->bits & FALLING_EDGE_ROTATING_MASK) >> 4) ==
((state->bits & RISING_EDGE_ROTATING_MASK) >> 6)) {
if ((state->bits & RISING_EDGE_ROTATING_MASK) == RISING_EDGE_CLOCKWISE) {
delta = -1;
} else {
delta = 1;
}
state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) |
FALLING_EDGE_NOT_ROTATING;
} else {
delta = 0;
if ((state->bits & FALLING_EDGE_ROTATING_MASK) !=
FALLING_EDGE_NOT_ROTATING) {
state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) |
FALLING_EDGE_NOT_ROTATING;
}
}
} else {
state->bits = (state->bits & (~FALLING_EDGE_ROTATING_MASK)) | 0x20 |
(state->value >> 2 & 1) << 4;
if (((state->bits & FALLING_EDGE_ROTATING_MASK) >> 4) ==
((state->bits & RISING_EDGE_ROTATING_MASK) >> 6)) {
if ((state->bits & FALLING_EDGE_ROTATING_MASK) ==
FALLING_EDGE_CLOCKWISE) {
delta = -1;
} else {
delta = 1;
}
state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) |
RISING_EDGE_NOT_ROTATING;
} else {
delta = 0;
if ((state->bits & RISING_EDGE_ROTATING_MASK) !=
RISING_EDGE_NOT_ROTATING) {
state->bits = (state->bits & (~RISING_EDGE_ROTATING_MASK)) |
RISING_EDGE_NOT_ROTATING;
}
}
}
if (delta == 0) {
return;
}
s_ui_input_t event = {.value = delta};
if (state->key == ui_input_raw_key_encoder_0) {
ec0_interval = esp_timer_get_time() - ec0_last_time;
if (ec0_interval < 10000) { // 100ms
event.value = event.value * 5;
} else if (ec0_interval < 20000) { // 100ms
event.value = event.value * 4;
} else if (ec0_interval < 50000) { // 100ms
event.value = event.value * 3;
} else if (ec0_interval < 100000) { // 100ms
event.value = event.value * 2;
}
ec0_last_time = esp_timer_get_time();
if (state->value & 1) {
event.key = ui_input_key_computer_volume;
} else {
event.key = ui_input_key_display_1_brightness;
}
} else if (state->key == ui_input_raw_key_encoder_1) {
if (state->value & 1) {
ec1_interval = esp_timer_get_time() - ec1_last_time;
if (ec1_interval < 20000) { // 100ms
event.value = event.value * 7;
} else if (ec1_interval < 30000) { // 100ms
event.value = event.value * 5;
} else if (ec1_interval < 40000) { // 100ms
event.value = event.value * 3;
} else if (ec1_interval < 50000) {
event.value = event.value * 2;
}
ec1_last_time = esp_timer_get_time();
event.key = ui_input_key_display_ambient_lighting_level;
led_strip_set_brightness(display_ambient_lighting_level + event.value);
gui_change_strip_level(display_ambient_lighting_level);
} else {
event.key = ui_input_key_display_0_brightness;
}
}
xQueueSend(ui_input_event, &event, NULL);
ESP_LOGD(UI_INPUT_TAG, "key: %d, delta: %d. delay: %lld, %lld", state->key,
event.value, ec0_interval, ec1_interval);
}
static void ui_input_raw_handler(void *arg) {
for (;;) {
if (xQueueReceive(ui_input_raw_event, NULL, portMAX_DELAY)) {
pca9555_read_one_input(PCA95555_CMD_INPUT_PORT_1, &level_byte);
encoder_0_state.value = level_byte & 0x7;
encoder_value_change(&encoder_0_state);
encoder_1_state.value = level_byte >> 3 & 0x7;
encoder_value_change(&encoder_1_state);
}
}
}
void ui_input_init(void) {
// zero-initialize the config structure.
gpio_config_t io_conf = {};
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
// interrupt of rising edge
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = 1ULL << ENCODER_INT_GPIO;
gpio_config(&io_conf);
// start encoder task
ui_input_event = xQueueCreate(5, sizeof(s_ui_input_t));
ui_input_raw_event = xQueueCreate(10, 0);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(ENCODER_INT_GPIO, gpio_isr_handler, NULL);
// xTaskCreate(ui_input_update_embedded_display, "ui_input_event", 2048, NULL,
// 10, NULL);
xTaskCreate(ui_input_raw_handler, "ui_input_event", 2048, NULL, 10, NULL);
}

View File

@@ -151,14 +151,7 @@ bool wifi_init_sta(void) {
} }
bool connect_wifi(void) { bool connect_wifi(void) {
// Initialize NVS //! Should init NVS before wifi init
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);
ESP_LOGI(WIFI_TAG, "ESP_WIFI_MODE_STA"); ESP_LOGI(WIFI_TAG, "ESP_WIFI_MODE_STA");
return wifi_init_sta(); return wifi_init_sta();