
赛博宫灯 - 基于ESP32-C3的装饰灯
简介
一款基于ESP32-C3 MCU设计的装饰灯,采用宫灯外形。
简介:一款基于ESP32-C3 MCU设计的装饰灯,采用宫灯外形。开源协议
:CC BY-NC-SA 4.0
(未经作者授权,禁止转载)描述
更新日志
2024.01.25:最初开源改项目 Ver1.0
2024.05.18:上传模型文件,请注意遵守开源规则,不可商用
零、模型说明
目前模型仍存在一定问题,后续修正并上传模型。
存在的问题(2024.01.25):
- 模型尺寸过小,需要放大到150%进行打印;
- 装配主要采用过盈装配,尺寸偏松,需要缩小尺寸;
- 顶檐连接处装配可靠性较差,修改装配方式。
每台3D打印机的打印效果不尽相同,需要根据打印机打印效果修改尺寸。
一、效果展示
1.1 说明
- 使用FDM-3D打印该模型
- 顶檐、灯栅采用白色PETG耗材打印
- 角饰、角件、顶饰采用蓝色PLA耗材打印
- 灯座采用薄荷绿PC/ABS耗材打印
(个人感觉采用浅黄色、黄色、橙色等暖色耗材打印会好看点,但是我手头没有该类颜色耗材)
1.2 组成构件明细
- 顶饰 x1
- EC11编码器 x1(焊接XH2.54-4P端子 Key1、KeyA、KeyB、GND,详见下文4.1)
- 灯檐1、灯檐2、灯檐3 各1
- 磁铁 x4(用于灯檐磁吸装配)
- M3螺母 x4(用于灯檐磁吸装配)
- 角饰(灯角)x4
- 灯栅 x4(需要蒙纸 - 建议使用硫酸纸)
- Light PCB x1
- 灯角(灯下角)x4
- FPC 1.0x8P 反向 x1
- Controleer PCB x1
- M3x6螺丝 x4
- M3土八螺母/铜嵌螺母 x4
- 聚合物电池(尺寸65x55x9.5mm PH2.0-2P端子)x1
- 灯座 x1
Figure1 实物图1(找不到硫酸纸用活页纸随便凑合了)
Figure2 实物图2
Figure3 实物爆炸图
二、硬件设计
2.0 设计思路
最初参照床头灯功能进行设计,需要具有:发光、带有电池与电源管理、能够方便开关与调节亮度并预留物联网功能。
本项目沿用自月球灯项目,对其进行大幅删改与适配。
各部分设计思路见下文:
2.1 MCU
为实现灯光控制、物联网等功能,采用ESP32-C3-MINI-N4作为控制器。
我永远喜欢乐鑫的USB JTAG,USB插上就能烧录是真滴爽
无需额外增加芯片,只需使用USB线与PC相连,即可进行烧录。
2.2 灯光
灯光部分位于独立的PCB上(板子Light)。
采用铝基板作为板材,保证长时间照明下灯珠不会严重过热,提升灯珠寿命。
发光分为两部分:白色与彩色。
Figure4 Lgiht-PCB
- 白色灯光
为了适配不同的应用场合,白色分别使用了两种色温的灯珠,能够组合得到不同色温的白色。
(由于我选用的型号没有封装库,故使用同一个3528封装LED代替,实际分别使用C2843808、C2843809)
采用4x4+4x4、串联驱动。
串联方案下,总线电流较小、每个灯珠间通过的电流相同,能够使灯珠发光亮度相近、寿命差别较小。
- 彩色灯光
要赛博怎么能少了R!G!B!
使用经典的WS2812b(5050封装)作为彩色灯光的照明来源。
采用4x3颗WS2812b作为彩色光源。
2.3 LED驱动
由于采用串联LED方案,对驱动电压的要求较高。
因此,需要输出电压范围较大、最高电压较高的方案。需选用升压型/升降压型LED驱动器。
白色照明分别由冷白、暖白两路LED组成,每路16颗LED串联。
通过查阅手册,可知每颗灯珠最大正向电压为3.2V、额定电流20mA。
因此额定驱动电压为3.2x16=51.2V(灯珠串联),需要能够升压至51.2V以上的驱动器。
额定电流20mA,一般驱动器都能满足该要求。
综上所述,需要选择升压型/升降压型、最大电压大于51.2V(还需留出一定冗余空间)的LED驱动方案。
通过比较,采用双路LGS63042 LED驱动器分别驱动暖白、冷白两路白色LED。驱动电路如下图所示:
Figure5 LED驱动电路
通过查阅3528 LED灯珠的手册,可知LED灯珠的正向电流为20mA。
依照Figure4中表格将Rsence设置为10Ω,设定满载输出电流为20mA。
LGS63042 LED驱动器支持PWM调光,可以通过向EN引脚输入PWM波对LED亮度进行调节。
2.4 电源管理
电源管理方案沿用月球灯项目的相关设计。
2.5 人机交互
在人机交互方面,本项目采用了EC11编码器。EC11编码器带有一个正交编码器和一个非自锁式轻触开关。
编码器可以实现数值调节、模式调节等功能;轻触开关可以通过按键复用分别执行单击、双击、长按任务。
通过将以上交互任务进行组合,可以实现丰富的人机交互。
三、软件设计
根据以上硬件设计,系统整体框架如下图所示:
Figure6 系统框架
当前实现的功能较为简单,主要实现了整体的亮度调节、WS2812b灯珠的调色。
后续会进一步实现自动配网、物联网以及更流畅的灯控等功能。
也有大佬在我的另一个项目里提到WLED,等我了解一下考虑要不要移植。
3.1 系统的刷新
设置一个定时器,并设定Ts
ms为刷新周期,每一个Ts
周期刷新一次灯珠发光状态(LED1、LED2亮度;LED3色相、亮度)。
3.2 WS2812b灯珠驱动
与月球灯项目相似,本项目使用FastLED库
实现了WS2812b灯珠的驱动。
目前主要实现了色彩控制与亮度控制:
-
色彩控制
WS2812b的色彩基于HSV色彩模型进行控制。
每一种颜色都是由色相(Hue,简H),饱和度(Saturation,简S)和色明度(Value,简V)所表示的。
默认饱和度、明度都设置为255,通过控制色相改变色彩。
自定义了一种HSV颜色CHSV myHSVcolor(hVal,sVal,vVal);
,通过myHSVcolor.h+=x
(x指数值)改变色相。
最后通过FastLED.show();
函数刷新WS2812b的发光状态。 -
亮度控制
设置了一个unsigned char
变量Light3Level
,用于存储亮度数值。
通过FastLED.setBrightness(Light3Level);
设置存储的亮度数值。
最后通过FastLED.show();
函数刷新WS2812b的发光状态。
3.3 白色灯珠驱动
白色LED通过LGS63042驱动电路进行驱动,可以通过PWM进行调光。
使用analogWrite(LED1,Light1Level);
函数设定对驱动芯片EN引脚输入PWM波的占空比,范围0~255。
3.4 EC11编码器
EC11编码器分为正交编码器相关程序设计和轻触开关程序设计。
人机交互逻辑如下:
Figure7 人机交互逻辑
-
正交编码器(旋钮):
正交编码器的交互通过Encoder库
实现。
首先通过编码器两个引脚注册编码器Encoder Enc(KEYB, KEYA);
。
设置一个初始位置后,通过Encoder库Enc.read();
读取新的位置newPosition
,
将新的位置与旧的位置oldPosition
比较,并做对应数值增减。 -
轻触开关(按键):
轻触开关的交互通过OneButton库
实现。
如Figure7所示,按键分为单击、双击、长按三个功能,并分别复用任务。
四、模型装配
构成见1.2 组成构件明细。
-
角饰、灯角、灯檐、灯栅、灯座的装配:
模型主要采用过盈装配。角饰、灯角带有V型槽,可以卡入灯栅;灯檐带有榫卯;灯座带有槽恰好可以卡入灯角。 -
角饰-灯檐的装配:
灯檐与角饰见采用磁吸进行连接组合,需要在角饰内埋入M3螺母、顶檐1埋入Φ6x3mm磁铁。 -
灯角-Lgiht PCB、灯座-Controler PCB的装配:
灯座与灯角需要埋入土八螺母/铜嵌螺母,用于M3螺纹配合,以固定Controler、Light两块PCB。(见Figure10、Figure11) -
顶饰-灯檐3-EC11编码器的装配:
EC11编码器带有螺纹,通过配套的螺母将EC11编码器固定在顶檐3上;
EC11编码器旋钮杆恰好可以塞入顶饰孔中,保留一定距离用于EC11上轻触开关的键程。(见Figure12) -
顶饰EC11编码器的接线焊接:
将正交编码器的公共端、轻触开关其中一段与EC11编码器底部铁片焊接在一起。
焊接四根线,分别为KeyA、KeyB、Key1、GND。
正交编码器另外两端分别焊接KeyA、KeyB;
EC11编码器固定脚(任一)焊接GND;
轻触开关另一端焊接Key1。
Figure8 三维模型
Figure9 三维模型爆炸图
Figure10 灯座-控制器图1
Figure11 灯座-控制器图2
Figure12 灯檐3-EC11编码器
五、更多图片
Figure13 灯座正面(Type-C输入接口与充电指示灯)
Figure14 灯座背面(EN复位按键-需要使用顶针)
Figure15 灯顶(灯饰作为EC11键帽,能够旋转与按下)
附录
I.源代码
#include
#include
#include
#include
// 引脚设置
#define LED1 0
#define LED2 1
#define KEY1 5
#define KEYA 6
#define KEYB 7
// 参数设置
// #define freq 500
// #define resolution 8
#define Ts 50
#define NUM_LEDS 12
#define DATA_PIN 10
#define LED_TYPE WS2812
#define COLOR_ORDER GRB
boolean WorkStatus=0;
unsigned char ModeStatus=0;
unsigned char Light1Level=0;
unsigned char Light2Level=0;
unsigned char Light3Level=0;
unsigned char hVal=0;
unsigned char sVal=255;
unsigned char vVal=255;
// Fastled
CRGB leds[NUM_LEDS];
CHSV myHSVcolor(hVal,sVal,vVal);
// Encoder
Encoder Enc(KEYB, KEYA);
long oldPosition=-999;
long newPosition=0;
void EncoderRead(){
newPosition = Enc.read();
if(newPosition != oldPosition){
if(newPosition > oldPosition){
//LED1
if(Light1Level>=255){
Light1Level=255;
}
else{
Light1Level++;
}
//LED2
if(Light2Level>=255){
Light2Level=255;
}
else{
Light2Level++;
}
//LED3
if(Light3Level>=255){
Light3Level=255;
}
else{
Light3Level++;
}
}
else{
//LED1
if(Light1Level<=0){
Light1Level=0;
}
else{
Light1Level--;
}
//LED2
if(Light2Level<=0){
Light2Level=0;
}
else{
Light2Level--;
}
//LED3
if(Light3Level<=0){
Light3Level=0;
}
else{
Light3Level--;
}
}
oldPosition=newPosition;
}
}
// OneButton
OneButton button1(KEY1, true);
void Button1Click(){
Light1Level=0;
Light2Level=0;
Light3Level=0;
}
void Button1DoubleClick(){
myHSVcolor.h+=8;
fill_solid(leds, NUM_LEDS, myHSVcolor);
}
void Button1LongPress(){
Light1Level=255;
Light2Level=255;
Light3Level=255;
}
// LED
void LED1Flash(){
analogWrite(LED1,Light1Level);
}
void LED2Flash(){
analogWrite(LED2,Light2Level);
}
void LED3Flash(){
FastLED.setBrightness(Light3Level);
}
// 定时器设置
hw_timer_t * timer1 = NULL; // 定时器1 采样、上传、控制刷新
unsigned char level=0;
void IRAM_ATTR onTimer1(){
// analogWrite(LED1,level);
// analogWrite(LED2,level);
// level+=16;
LED1Flash();
LED2Flash();
LED3Flash();
}
void setup() {
// put your setup code here, to run once:
// 串口初始化
//Serial.begin(115200);
// 引脚初始化
pinMode(LED1,OUTPUT);
pinMode(LED2,OUTPUT);
// OneButton
button1.attachClick(Button1Click);
button1.attachDoubleClick(Button1DoubleClick);
button1.attachLongPressStart(Button1LongPress);
// FastLED
FastLED.addLeds(leds, NUM_LEDS);
FastLED.setBrightness(128);
// 定时器初始化
// 定时器1
timer1 = timerBegin(1,80,true); // 初始化定时器-使用定时器1
timerAttachInterrupt(timer1,onTimer1,true); // 绑定定时器中断服务函数
timerAlarmWrite(timer1,Ts*1000,true); // 设置中断 间隔为采样周期
timerAlarmEnable(timer1); // 启动定时器
}
void loop() {
// put your main code here, to run repeatedly:
button1.tick();
FastLED.show();
EncoderRead();
}
设计图

BOM


评论