更新日志
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 系统的刷新
设置一个定时器,并设定Tsms为刷新周期,每一个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 port initialization
//Serial.begin(115200);
// Pin initialization
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);
// Timer initialization
// Timer1
timer1 = timerBegin(1,80,true); // Initialize timer - use timer1
timerAttachInterrupt(timer1,onTimer1,true); // Bind timer interrupt service function
timerAlarmWrite(timer1,Ts*1000,true); // Set the interrupt interval to the sampling period:
`timerAlarmEnable(timer1);` // Start the timer.
} `
void loop() {
` // Put your main code here, to run repeatedly: `
button1.tick();
FastLED.show();
EncoderRead();
` }