37 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
31 changed files with 5458 additions and 762 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ build/
sdkconfig
sdkconfig.old
.vscode/
managed_components/
.DS_Store

View File

@ -4,7 +4,5 @@
# CMakeLists in this exact order for cmake to work correctly
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)
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 "pca9555.c" "i2c.c" "asr_pro.c" "ci_03t.c" "ui_input.c" "ambient_light.c" "temperature.c" "embedded_display.c" "mqtt.c" "main.c" "wifi.c" "light.c" "mqtt.c"
INCLUDE_DIRS ".")
idf_component_register(
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,43 +65,43 @@ menu "MQTT Configuration"
endmenu
menu "Encoder Configuration"
config ENCODER_0_CLK_PIN
int "encoder 0 clock GPIO"
range 0 32
config ENCODER_0_CLK_PORT_IO
int "encoder 0 clock IO"
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
help
Encoder 0 clock pin
config ENCODER_0_DT_PIN
int "encoder 0 data GPIO"
range 0 32
default 3
help
Encoder 0 clock data
config ENCODER_0_SW_PIN
int "encoder 0 switch GPIO"
range 0 32
config ENCODER_0_SW_PORT_IO
int "encoder 0 switch IO"
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
help
Encoder 0 switch pin
config ENCODER_1_CLK_PIN
int "encoder 1 clock GPIO"
range 0 32
Encoder 1 clock io on PCA9555 port 1
config ENCODER_1_DT_PORT_IO
int "encoder 1 data IO"
range 0 7
default 5
help
Encoder 1 clock pin
config ENCODER_1_DT_PIN
int "encoder 1 data GPIO"
range 0 32
default 6
help
Encoder 1 clock data
config ENCODER_1_SW_PIN
int "encoder 1 switch GPIO"
range 0 32
default 7
config ENCODER_1_SW_PORT_IO
int "encoder 1 switch IO"
range 0 7
default 3
help
Encoder 1 switch pin
Encoder 1 switch io on PCA9555 port 1
endmenu
@ -109,13 +109,13 @@ menu "I2C Configuration"
config I2C_SCL
int "I2C SCL GPIO"
range 0 32
default 8
default 5
help
I2C SCL GPIO
config I2C_SDA
int "I2C SDA GPIO"
range 0 32
default 10
default 4
help
I2C SDA GPIO
config I2C_NUM
@ -146,3 +146,12 @@ menu "UART Configuration"
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

@ -120,9 +120,9 @@ void ambient_light_fetch(void* arg) {
uint16_t als_ch0_raw;
uint16_t als_ch1_raw;
uint16_t proximity_raw;
char als_ch0_str[10];
char als_ch1_str[10];
char proximity_str[10];
char als_ch0_str[20];
char als_ch1_str[20];
char proximity_str[20];
uint8_t als_ch0_buffer[] = {0, 0};
uint8_t als_ch1_buffer[] = {0, 0};
uint8_t proximity_buffer[] = {0, 0};
@ -183,7 +183,7 @@ void ambient_light_auto_fetch() {
void ambient_light_init() {
if (is_apds_9930_online == 0) {
ESP_LOGI(AMBIENT_LIGHT_TAG, "AMG8833 is offline");
ESP_LOGI(AMBIENT_LIGHT_TAG, "APDS 9930 is offline");
return;
}
ESP_LOGI(AMBIENT_LIGHT_TAG, "Initializing APDS-9930");

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);
}

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);
}

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); \
} \
}

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,143 +0,0 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "codetab.h"
#include "config_key.h"
#include "driver/i2c.h"
#include "esp_log.h"
#include "i2c.c"
#define Brightness 0xCF
#define X_WIDTH 128
#define Y_WIDTH 64
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 << 1 | 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[]) {
if (!is_embedded_display_online) return;
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() {
if (is_embedded_display_online == 0) {
ESP_LOGE("display", "display is offline");
return;
}
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);
}
void gui_update_config_uint8(uint8_t key, uint8_t value) {
char changing_str[12] = "NC";
switch (key) {
case ui_input_key_display_0_brightness:
sprintf(changing_str, "Dis0: % 3d", value);
break;
case ui_input_key_display_1_brightness:
sprintf(changing_str, "Dis1: % 3d", value);
break;
case ui_input_key_computer_volume:
sprintf(changing_str, "CVol: % 3d", value);
break;
case ui_input_key_display_ambient_lighting_level:
sprintf(changing_str, "ALLv: % 3d", value);
break;
case ui_input_key_display_ambient_lighting_mode:
sprintf(changing_str, "ALMd: % 3d", value);
break;
case ui_input_key_display_0_mode:
sprintf(changing_str, "Dis0M: % 2d", value);
break;
case ui_input_key_display_1_mode:
sprintf(changing_str, "Dis1M: % 2d", value);
break;
default:
strcpy(changing_str, "NC");
break;
}
display_fill_rect(0, 6, 128, 8, 0);
display_print8_str(8, 6, changing_str);
}

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);
}

View File

@ -20,13 +20,15 @@
#define PCA9555_ADDRESS 0x20
#define GX21M15_ADDRESS 0x48
#define APDS_9930_ADDRESS 0x39
#define I2C_ADDRESS 0x3c
#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() {
@ -52,7 +54,7 @@ uint8_t i2c_check_slave_exists(uint8_t address) {
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_RATE_MS);
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) {
@ -71,5 +73,6 @@ 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_embedded_display_online = i2c_check_slave_exists(I2C_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,25 +1,22 @@
/* RMT example -- RGB LED Strip
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#pragma once
#include "driver/rmt.h"
#include <string.h>
#include "driver/rmt_tx.h"
#include "esp_log.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.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"
static const char *LIGHT_TAG = "DisplayAmbientLight_Light";
#define RMT_TX_CHANNEL RMT_CHANNEL_0
#define RMT_TX_GPIO 1
#define STRIP_LED_NUMBER CONFIG_NUMBER_OF_LEDS
#define EXAMPLE_CHASE_SPEED_MS (10)
#define RMT_LED_STRIP_RESOLUTION_HZ 10000000
typedef enum light_mode_e {
light_mode_init = 0,
@ -31,11 +28,21 @@ typedef enum light_mode_e {
light_mode_off = 6,
} 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;
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;
@ -55,6 +62,78 @@ void led_strip_set_brightness(uint8_t 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));
}
}
/**
@ -110,68 +189,111 @@ void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r,
}
}
void update_desktop_connection_state() {
static uint8_t tick = 0;
// void update_desktop_connection_state() {
// static uint8_t tick = 0;
bool beat = tick / 10 % 2 ? 1 : 0;
// bool beat = tick / 10 % 2 ? 1 : 0;
switch (light_mode) {
case light_mode_desktop_online:
if (beat) {
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10));
}
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10));
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 2, 10, 10, 10));
break;
case light_mode_mqtt_connected:
if (beat) {
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10));
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10));
}
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 2, 22, 22, 22));
break;
case light_mode_idle:
if (beat) {
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77));
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, 77, 77, 77));
}
break;
default:
break;
}
// switch (light_mode) {
// case light_mode_desktop_online:
// if (beat) {
// led_strip_pixels[0] = 0;
// led_strip_pixels[1] = 0;
// led_strip_pixels[2] = 0;
// }
// led_strip_pixels[3] = 10
// break;
// case light_mode_mqtt_connected:
// if (beat) {
// ESP_ERROR_CHECK(
// light_led_strip->set_pixel(light_led_strip, 0, 10, 10, 10));
// ESP_ERROR_CHECK(
// light_led_strip->set_pixel(light_led_strip, 1, 10, 10, 10));
// }
// ESP_ERROR_CHECK(
// light_led_strip->set_pixel(light_led_strip, 2, 22, 22, 22));
// break;
// case light_mode_idle:
// if (beat) {
// ESP_ERROR_CHECK(
// light_led_strip->set_pixel(light_led_strip, 0, 77, 77, 77));
// 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, 77, 77, 77));
// }
// break;
// default:
// break;
// }
tick++;
}
// tick++;
// }
void 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;
int8_t i = 0;
memset(led_strip_pixels, 0, sizeof(led_strip_pixels));
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 {
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++) {
led_strip_hsv2rgb(0, 0, i, &red, &green, &blue);
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, j, red, green, blue));
led_strip_pixels[j * 3 + 0] = init_g;
led_strip_pixels[j * 3 + 1] = init_r;
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);
led_strip_set_color_calibration((float)r / 255.0, (float)g / 255.0,
(float)b / 255.0);
}
void light_for_connecting_wifi() {
@ -180,11 +302,21 @@ void light_for_connecting_wifi() {
int8_t tick_tock = 0;
do {
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, tick_tock, 150, 150, 0));
ESP_ERROR_CHECK(light_led_strip->set_pixel(light_led_strip,
(tick_tock + 1) % 2, 0, 200, 0));
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100));
if (tick_tock) {
led_strip_pixels[0] = 150;
led_strip_pixels[1] = 150;
led_strip_pixels[2] = 0;
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;
vTaskDelay(pdMS_TO_TICKS(200));
} while (light_mode == light_mode_connection_wifi);
@ -192,7 +324,10 @@ void light_for_connecting_wifi() {
void 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;
uint16_t step_length = 360 / STRIP_LED_NUMBER;
@ -206,22 +341,21 @@ void light_for_idle() {
// Build RGB values
led_strip_hsv2rgb(hue, 50, 30, &red, &green, &blue);
red = red * display_ambient_light_brightness;
green = green * display_ambient_light_brightness;
blue = blue * display_ambient_light_brightness;
// Write RGB values to strip driver
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, j, red, green, blue));
led_strip_pixels[j * 3 + 0] = green * display_ambient_light_brightness *
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();
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100));
// update_desktop_connection_state();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void light_strip_running_task(void *pv_parameters) {
while (true) {
if (!light_led_strip) {
if (!led_chan) {
ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed 2");
}
switch (light_mode) {
@ -245,47 +379,76 @@ void light_strip_running_task(void *pv_parameters) {
}
void light_init_strip() {
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_TX_GPIO, RMT_TX_CHANNEL);
// set counter clock to 40MHz
config.clk_div = 2;
rmt_tx_channel_config_t tx_chan_config = {
.clk_src = RMT_CLK_SRC_DEFAULT, // select source clock
.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_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
ESP_LOGI(LIGHT_TAG, "Install led strip encoder");
// install ws2812 driver
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(
STRIP_LED_NUMBER, (led_strip_dev_t)config.channel);
light_led_strip = led_strip_new_rmt_ws2812(&strip_config);
if (!light_led_strip) {
ESP_LOGE(LIGHT_TAG, "install WS2812 driver failed");
}
// Clear LED strip (turn off all LEDs)
ESP_ERROR_CHECK(light_led_strip->clear(light_led_strip, 100));
// Show simple rainbow chasing pattern
ESP_LOGI(LIGHT_TAG, "LED Rainbow Chase Start");
led_strip_encoder_config_t encoder_config = {
.resolution = RMT_LED_STRIP_RESOLUTION_HZ,
};
ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&encoder_config, &led_encoder));
ESP_LOGI(LIGHT_TAG, "Enable RMT TX channel");
ESP_ERROR_CHECK(rmt_enable(led_chan));
ESP_LOGI(LIGHT_TAG, "Start LED rainbow chase");
light_mode = light_mode_init;
xTaskCreate(light_strip_running_task, "LIGHT_STRIP_RUNNING_TASK", 4096, 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) {
light_mode = light_mode_desktop_sending_colors;
// light_mode = light_mode_desktop_sending_colors;
for (uint16_t led_index = 0, buffer_cursor = 0;
led_index < STRIP_LED_NUMBER && buffer_cursor < len;
led_index++, buffer_cursor += 3) {
uint8_t r = (uint8_t)((float)buffer[buffer_cursor] *
display_ambient_light_brightness),
g = (uint8_t)((float)buffer[buffer_cursor + 1] *
display_ambient_light_brightness),
b = (uint8_t)((float)buffer[buffer_cursor + 2] *
display_ambient_light_brightness);
ESP_ERROR_CHECK(
light_led_strip->set_pixel(light_led_strip, led_index, r, g, b));
}
ESP_ERROR_CHECK(light_led_strip->refresh(light_led_strip, 100));
// uint16_t black_count = 0; // count of black pixels. r/g/b <= 10
// for (uint16_t led_index = 0, buffer_cursor = 0;
// led_index < STRIP_LED_NUMBER && buffer_cursor < len;
// led_index++, buffer_cursor += 3) {
// uint8_t r = (uint8_t)((float)buffer[buffer_cursor] *
// display_ambient_light_brightness *
// led_strip_red_calibration),
// 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));
}
@ -293,3 +456,53 @@ void light_play(light_mode_t mode) {
ESP_LOGI(LIGHT_TAG, "light_play: %d", 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,48 +1,69 @@
#include "ambient_light.c"
// #include "apds_9960.c"
#include "app_nvs.c"
#include "ch1116.c"
#include "ci_03t.c"
#include "embedded_display.c"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "gui.c"
#include "i2c.c"
#include "light.c"
#include "mqtt.c"
#include "pca9555.c"
// #include "mqtt.c"
// #include "pca9555.c"
#include "sdkconfig.h"
#include "temperature.c"
#include "service_discovery.c"
// #include "temperature.c"
#include "desktop.c"
#include "net_gateway_monitor.c"
#include "ui_input.c"
#include "wifi.c"
static const char *TAG = "DisplayAmbientLight";
static const char *APP_TAG = "DisplayAmbientLight";
void app_main(void) {
app_nvs_init();
light_init_strip();
gpio_install_isr_service(0);
init_i2c();
i2c_check_slaves();
init_display();
display_print8_str(0, 0, "Ambient Light");
ch1116_main();
gui_main();
gui_set_wifi_connecting();
gui_set_server_connecting();
// display_print8_str(0, 0, "Ambient Light");
ci_03t_init();
ambient_light_init();
ambient_light_auto_fetch();
auto_fetch_temperature();
// 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));
// xTaskCreate(mqtt_publish_ui_input, "mqtt_publish_ui_input", 2048, NULL, 10,
// NULL);
// vTaskDelay(pdMS_TO_TICKS(10));
light_play(light_mode_connection_wifi);
if (connect_wifi()) {
gui_set_wifi_connected();
light_play(light_mode_idle);
} else {
gui_set_wifi_disconnected();
}
vTaskDelay(pdMS_TO_TICKS(1000));
mqtt_app_start();
if (waiting_for_mqtt_connected()) {
light_play(light_mode_mqtt_connected);
}
if (waiting_for_desktop_online()) {
light_play(light_mode_desktop_online);
}
while (waiting_and_get_colors()) {
light_play_colors(NUMBER_OF_LEDS * 3, mqtt_colors_buffer);
}
udp_server_init();
net_gateway_monitor_init();
service_discovery_init();
// vTaskDelay(pdMS_TO_TICKS(1000));
// mqtt_app_start();
// if (waiting_for_mqtt_connected()) {
// light_play(light_mode_mqtt_connected);
// }
// if (waiting_for_desktop_online()) {
// light_play(light_mode_desktop_online);
// }
// while (waiting_and_get_colors()) {
// light_play_colors(NUMBER_OF_LEDS * 3, mqtt_colors_buffer);
// }
}

View File

@ -1,281 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include "ci_03t.c"
#include "embedded_display.c"
#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_input.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_CMD_SUFFIX "cmd"
#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
#define MQTT_KEY_BOARD_CMD MQTT_BOARD_KEY_PREFIX MQTT_CMD_SUFFIX
#define MQTT_KEY_DESKTOP_DISPLAY_0_BRIGHTNESS \
MQTT_DESKTOP_KEY_PREFIX "display0/brightness"
#define MQTT_KEY_DESKTOP_DISPLAY_1_BRIGHTNESS \
MQTT_DESKTOP_KEY_PREFIX "display1/brightness"
static const char *MQTT_TAG = "DisplayAmbientLight_MQTT";
static EventGroupHandle_t s_mqtt_event_group;
static xQueueHandle mqtt_cmd_event = NULL;
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 {
if (strncmp(event->topic, MQTT_KEY_DESKTOP_DISPLAY_0_BRIGHTNESS,
event->topic_len) == 0) {
s_ui_input_t mqtt_event = {
.key = ui_input_key_display_0_brightness,
.value = (uint16_t)(event->data[0] << 8 | event->data[1]),
};
xQueueSend(mqtt_cmd_event, &mqtt_event, NULL);
} else if (strncmp(event->topic, MQTT_KEY_DESKTOP_DISPLAY_1_BRIGHTNESS,
event->topic_len) == 0) {
s_ui_input_t mqtt_event = {
.key = ui_input_key_display_1_brightness,
.value = (uint16_t)(event->data[0] << 8 | event->data[1]),
};
xQueueSend(mqtt_cmd_event, &mqtt_event, NULL);
} 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_cmd_event_handler(void *arg) {
s_ui_input_t event;
for (;;) {
if (xQueueReceive(mqtt_cmd_event, &event, portMAX_DELAY)) {
ESP_LOGI(MQTT_TAG, "mqtt_cmd_event_handler");
gui_update_config_uint8(ui_input_key_display_0_brightness,
(uint8_t)(event.value & 0xFF));
switch (event.key) {
case ui_input_key_display_0_brightness: {
ESP_LOGI(MQTT_TAG, "ui_input_key_display_0_brightness %x",
event.value);
uint8_t data[] = {0x02, (uint8_t)(event.value & 0xFF)};
ci_03t_send_data(data, sizeof(data));
break;
}
case ui_input_key_display_1_brightness: {
ESP_LOGI(MQTT_TAG, "ui_input_key_display_0_brightness %x",
event.value);
uint8_t data[] = {0x03, (uint8_t)(event.value & 0xFF)};
ci_03t_send_data(data, sizeof(data));
break;
}
default:
break;
}
}
}
}
static void mqtt_app_start() {
mqtt_colors_buffer = (uint8_t *)malloc(sizeof(uint8_t) * NUMBER_OF_LEDS * 3);
s_mqtt_event_group = xEventGroupCreate();
mqtt_cmd_event = xQueueCreate(10, sizeof(s_ui_input_t));
xTaskCreate(mqtt_cmd_event_handler, "mqtt_cmd_event", 2048, NULL, 10, NULL);
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 mqtt_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_key_display_0_brightness:
case ui_input_key_display_1_brightness: {
cJSON *publish_json_root, *brightness;
publish_json_root = cJSON_CreateObject();
cJSON_AddNumberToObject(
publish_json_root, "display_index",
ui_input_key_display_0_brightness == input.key ? 0 : 1);
cJSON_AddItemToObject(publish_json_root, "brightness",
brightness = cJSON_CreateObject());
cJSON_AddNumberToObject(brightness, "Relative", 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);
}

View File

@ -1,8 +1,8 @@
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include "driver/i2c.h"
#include "embedded_display.c"
#include "esp_log.h"
#include "i2c.c"
@ -19,8 +19,82 @@
#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) {
@ -28,5 +102,6 @@ void pca9555_init() {
return;
}
ESP_LOGI(PCA_9555_TAG, "Initializing PCA9555");
// esp_log_level_set(PCA_9555_TAG, ESP_LOG_ERROR);
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

@ -22,7 +22,7 @@
void fetch_temperature(void* arg) {
esp_err_t error;
float temperature = DEFAULT_TEMPERATURE;
char temperature_str[10];
char temperature_str[20];
uint8_t temperature_buffer[] = {0, 0};
display_fill_rect(0, 0, 128, 2, 0x00);
for (;;) {

View File

@ -6,76 +6,63 @@
#include "config_key.h"
#include "driver/gpio.h"
#include "embedded_display.c"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "gui.c"
#include "light.c"
#include "pca9555.c"
#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_SW_PIN CONFIG_ENCODER_0_SW_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_0_SW_PIN_MASK 1ULL << ENCODER_0_SW_PIN
#define ENCODER_1_CLK_PIN CONFIG_ENCODER_1_CLK_PIN
#define ENCODER_1_DT_PIN CONFIG_ENCODER_1_DT_PIN
#define ENCODER_1_SW_PIN CONFIG_ENCODER_1_SW_PIN
#define ENCODER_1_CLK_PIN_MASK 1ULL << ENCODER_1_CLK_PIN
#define ENCODER_1_DT_PIN_MASK 1ULL << ENCODER_1_DT_PIN
#define ENCODER_1_SW_PIN_MASK 1ULL << ENCODER_1_SW_PIN
#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 NOT_ROTATING -1
#define CLOCKWISE 1
#define COUNTER_CLOCKWISE 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 xQueueHandle ui_input_event = NULL;
static xQueueHandle ui_input_raw_event = NULL;
static QueueHandle_t ui_input_event = NULL;
static QueueHandle_t ui_input_raw_event = NULL;
static int8_t rising_edge_rotation_direction = NOT_ROTATING;
static int8_t falling_edge_rotation_direction = NOT_ROTATING;
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;
uint8_t input_key;
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) {
input_key = (e_ui_input_raw_key_t)arg;
switch (input_key) {
case ui_input_raw_key_encoder_0: {
s_ui_input_raw_t event = {
.key = input_key,
.value = (gpio_get_level(ENCODER_0_SW_PIN) << 2) |
(gpio_get_level(ENCODER_0_CLK_PIN) << 1) |
(gpio_get_level(ENCODER_0_DT_PIN)),
};
xQueueSendFromISR(ui_input_raw_event, &event, NULL);
break;
}
case ui_input_raw_key_encoder_1: {
s_ui_input_raw_t event = {
.key = input_key,
.value = (gpio_get_level(ENCODER_1_SW_PIN) << 2) |
(gpio_get_level(ENCODER_1_CLK_PIN) << 1) |
(gpio_get_level(ENCODER_1_DT_PIN)),
};
xQueueSendFromISR(ui_input_raw_event, &event, NULL);
break;
}
default:
return;
}
xQueueSendFromISR(ui_input_raw_event, NULL, NULL);
}
static void ui_input_update_embedded_display(void *arg) {
s_ui_input_t input;
char changing_str[12] = "NC";
char changing_str[20] = "NC";
for (;;) {
if (xQueueReceive(ui_input_event, &input, portMAX_DELAY)) {
switch (input.key) {
@ -89,7 +76,6 @@ static void ui_input_update_embedded_display(void *arg) {
sprintf(changing_str, "CVol: % 3d", input.value);
break;
case ui_input_key_display_ambient_lighting_level:
sprintf(changing_str, "ALLv: % 3d", display_ambient_lighting_level);
break;
case ui_input_key_display_ambient_lighting_mode:
sprintf(changing_str, "ALMd: % 3d", input.value);
@ -105,97 +91,120 @@ static void ui_input_update_embedded_display(void *arg) {
strcpy(changing_str, "NC");
break;
}
display_fill_rect(0, 6, 128, 8, 0);
display_print8_str(8, 6, changing_str);
ESP_LOGI(UI_INPUT_TAG, "%s", changing_str);
}
}
}
static void ui_input_raw_handler(void *arg) {
s_ui_input_raw_t input;
uint8_t clk_level;
uint8_t dt_level;
uint8_t sw_level;
int8_t delta = 0;
char changing_str[12] = "NC";
for (;;) {
if (xQueueReceive(ui_input_raw_event, &input, portMAX_DELAY)) {
sw_level = input.value >> 2 & 1;
clk_level = input.value >> 1 & 1;
dt_level = input.value & 1;
if (clk_level) {
rising_edge_rotation_direction = dt_level == 0;
if (falling_edge_rotation_direction == rising_edge_rotation_direction) {
if (rising_edge_rotation_direction) {
delta = 1;
} else {
delta = -1;
}
falling_edge_rotation_direction = NOT_ROTATING;
} else {
delta = 0;
if (falling_edge_rotation_direction != NOT_ROTATING) {
rising_edge_rotation_direction = NOT_ROTATING;
}
}
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 {
falling_edge_rotation_direction = dt_level;
if (rising_edge_rotation_direction == falling_edge_rotation_direction) {
if (falling_edge_rotation_direction) {
delta = 1;
} else {
delta = -1;
}
rising_edge_rotation_direction = NOT_ROTATING;
} else {
delta = 0;
if (rising_edge_rotation_direction != NOT_ROTATING) {
falling_edge_rotation_direction = NOT_ROTATING;
}
}
delta = 1;
}
if (delta == 0) {
continue;
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;
}
// display_fill_rect(0, 6, 128, 8, 0);
// switch (input.key) {
// case ui_input_raw_key_encoder_0:
// sprintf(changing_str, "E0 %d: %u%u%u%u%u", delta,
// (input.value >> 4) & 1, (input.value >> 3) & 1,
// (input.value >> 2) & 1, (input.value >> 1) & 1,
// (input.value) & 1);
// break;
// case ui_input_raw_key_encoder_1:
// sprintf(changing_str, "E1 %d: %u%u%u%u%u", delta,
// (input.value >> 4) & 1, (input.value >> 3) & 1,
// (input.value >> 2) & 1, (input.value >> 1) & 1,
// (input.value) & 1);
// break;
// default:
// strcpy(changing_str, "NC");
// break;
// }
// display_print8_str(8, 6, changing_str);
s_ui_input_t event = {.value = delta};
if (input.key == ui_input_raw_key_encoder_0) {
if (sw_level) {
event.key = ui_input_key_computer_volume;
} else {
event.key = ui_input_key_display_0_brightness;
}
} else if (input.key == ui_input_raw_key_encoder_1) {
if (sw_level) {
event.key = ui_input_key_display_ambient_lighting_level;
led_strip_set_brightness(display_ambient_lighting_level + delta);
} else {
event.key = ui_input_key_display_1_brightness;
}
}
} 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;
}
xQueueSendFromISR(ui_input_event, &event, NULL);
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);
}
}
}
@ -208,31 +217,18 @@ void ui_input_init(void) {
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);
io_conf.pull_up_en = 1;
io_conf.pull_down_en = 0;
io_conf.pin_bit_mask = ENCODER_0_SW_PIN_MASK | ENCODER_1_SW_PIN_MASK;
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(10, sizeof(s_ui_input_t));
ui_input_raw_event = xQueueCreate(10, sizeof(s_ui_input_raw_t));
ui_input_event = xQueueCreate(5, sizeof(s_ui_input_t));
ui_input_raw_event = xQueueCreate(10, 0);
// 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_raw_key_encoder_0);
// hook isr handler for specific gpio pin
gpio_isr_handler_add(ENCODER_1_CLK_PIN, gpio_isr_handler,
(void *)ui_input_raw_key_encoder_1);
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_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) {
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
//! Should init NVS before wifi init
ESP_LOGI(WIFI_TAG, "ESP_WIFI_MODE_STA");
return wifi_init_sta();