发作品签到
专业版

智能UV固化灯(PCB阻焊绿油维修专用)

工程标签

5.8k
0
0
29

简介

一款时间亮度可控的智能UV固化灯

简介:一款时间亮度可控的智能UV固化灯
星火计划2024

开源协议

GPL 3.0

(未经作者授权,禁止转载)
创建时间:2024-01-19 08:05:46更新时间:2024-09-30 05:47:05

描述

演示视频

DIY智能UV灯_哔哩哔哩_bilibili

项目说明

UV灯在我们日常生活中并不陌生,他应用的领域非常广泛,比如:验钞,美甲,洗衣机杀菌,空气净化,UV胶水,3D打印机等等,然而作为一个电子DIY爱好者来说,我为什么要设计这么一个UV灯能?事情是这样的,我在一次维修电路板的时候,由于PCB的阻焊绿油被刮去了一部分,很容易造成短路,于是我买了阻焊绿油进行修补,但我发现卖家提供的UV灯功率特别小,烘烤时间也非常的长,主要绿油也干的很慢,对于像我这样用的电烙铁都是必须3秒钟升温到300度的急性子来说,这无疑是一个漫长的等待。于是就有了想自己DIY一个UV灯的想法。

那么既然有了想法,那就马上步入实践,我找变了某宝,目前PCB维修用的UV灯单个灯珠都在10W以下,但我觉得只是维修PCB使用的话,未免应用场景太小了,所以我开始

寻找适合我的UV灯珠,在经历了一天的海淘,我找到了合适的如图1所示(单个灯珠功率在15W)这就非常够用了。

> 图1:单个15W灯珠

那么新的问题来了,这个灯珠是6~8V供电,也就是说如果想要发挥这个UV灯的最大亮度,必须是8V供电,我最早想的是用一节18650锂电池搞定,但是现在看来单节锂电池只有3.7V满电才4.2V也就是说,至少需要两节电池串联,但是如果串联的话,体积就非常庞大了,而且携带和使用都不方便,那有没有非常符合要求的电池呢?答案是有的那就是航模电池(如图2:航模动力电池所示)

> 图2:航模动力电池

首先给大家普及一个知识,航模电池和普通电池的最大区别在于输出的电流大小,“C”用来表示电池充放电电流大小的比率,即倍率。充放电倍率=充放电电流/额定容量,如1200mAh的电池,0.2C表示240mA(1200mAh的0.2倍率),1C表示1200mA(1200mAh的1倍率)。(普通锂电池一般在0.5~1C,比如1000MAH容量的电池,1000mAX0.5=0.5A的放电电流)。

因此可以看到图2我框起来的地方写着20~70C,也就是说同样1000mAh容量的电池,放电电流等于:1000mAh X 20 = 20A,也就是说他的输出电流可以达到20A,那么对于15W的UV灯珠完全够用了。这里我选用了,(如图3锂电池型号所示)

> 图3:锂电池型号(他的体积和放电电流,刚好可以满足我的需要。)

PCB主要采用了立创EDA进行设计和打样,总的来说分为控制板(如图4)和铝基板(如图5)

> 图4:控制板

这个控制板主要集成功能有:

  • WIFI---用于配网和后续的APP控制
  • 串口下载接口---用于升级更新程序
  • OLED---用于显示相关信息
  • 锂电池保护电路---用于防止锂电池的过充过放
  • 锂电池充电电路---用于给串联锂电池充电
  • UV灯驱动电路---用于控制UV灯的亮度


>图5:铝基板

这个铝基板主要是由于UV灯在工作过程中,发热非常严重,直接焊接在PCB上,散热效果比较差,所以采用铝基板,至于为什么要做成这个形状,这和我买的如图6(散热风扇)的接触面积有关。

>图6:散热风扇

项目相关功能

设计原理

>原理图——主控部分

考虑成本和整体体积问题,这次选用ESP8266作为主控(外围电路简单,价格便宜),其实这个主控IO口有点不够用,这次的项目已经把所有IO口都用上了。简单介绍一下,这个主控如果需要正常工作需要将IO15下拉,IO2,IO0上拉,这里我将IO16也进行了上拉,因为这个引脚我接了按键(必须上拉)否则按键不起作用,这里通过使用一个五向按键来代替5个独立按键,极大节省PCB布局的摆放空间,这个flash芯片采用(W25Q32JVSSIQ)尽量靠近主控,减少干扰。至于主控的外围电路,可以参考ESP8266硬件数据手册,(直接照抄,靠谱!!!)。

PCB布局注意:
1.(RST,EN)电路要靠近芯片摆放,这是复位驱动电路,容易受到干扰。
2.(天线电路)尽可能的不要拐弯,(天线周围多打过孔)防止干扰。

>原理图——UV灯珠驱动电路

这个只是使用了一个简单的MOS管驱动的,在那之前我尝试了四五种驱动芯片,因为UV灯是8V供电,15W,也就是说电流约等于2A,所有效果都不理想。最后选用MOS管作为驱动,同时风扇已经没有多余的IO口了,也是使用的这个MOS管。LDO就不详细介绍了。

>原理图——充电部分

考虑到UV灯需要8V电压,因此采用两节3.7V锂电池串联的方式,充电芯片采用TP4256,可同时重两组串联的电池,电池保护电路,就是最普通的方案,这里就不过多介绍了,不懂的可以去看(HY2120-CB)的数据手册十分详细。

软件说明

为了方便大家复刻,采用了ArduinoIDE进行编程,我个人认为,这个程序的主要特点有两个:

1.图标的取模

这里采用的是iconfont-阿里巴巴矢量图标库
里面的图标特别全,完全可以满足日常需要,将需要的图标以PNG格式下载下来,并通过PS软件调整到适合OLED的分辨率,这里我调整到了32*32像素,后通过(PCtoLCD2002.exe )软件进行取模,取模完成后,就可以得到需要的数组了,大家可以根据需要设计适合自己的图标。通过修改(Text.h)文件进行图标的替换。

2.菜单的程序

本人才疏学浅,所以这个程序是和chatGPT共同完成的,为了方便理解,我会将程序进行中文注释:

首先:

  Serial.begin(115200);
  pinMode(UVdeng, OUTPUT);      // 设置UV灯引脚为输出模式
  pinMode(shang, INPUT_PULLUP); // 设置按键引脚为输入模式,并启用上拉电阻
  pinMode(xia, INPUT_PULLUP);  
  pinMode(zuo, INPUT_PULLUP);
  pinMode(you, INPUT_PULLUP);
  pinMode(zhong, INPUT_PULLUP);

  analogWrite(UVdeng, dutyCycle); //// 设置初始亮度

  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // 初始化OLED显示屏
    Serial.println(F("无法连接到SSD1306显示器"));
    for(;;);// 如果显示屏初始化失败,进入无限循环
  }
   timeClient.begin();

   strip.begin(); // 初始化WS2812
   strip.setBrightness(ambientBrightness); // 设置初始亮度
   strip.show(); // 初始化所有LED为关闭状态

  display.clearDisplay();      // 清除显示内容
  display.display();           // 刷新显示
  hans_display_1();            //开机LOGO
  delay(500);
 
  updateDisplay();            // 强制刷新显示内容
}

定义了按键与UV灯MOS管的控制引脚

  pinMode(UVdeng, OUTPUT);      // 设置UV灯引脚为输出模式
  pinMode(shang, INPUT_PULLUP); // 设置按键引脚为输入模式,并启用上拉电阻
  pinMode(xia, INPUT_PULLUP);  
  pinMode(zuo, INPUT_PULLUP);
  pinMode(you, INPUT_PULLUP);
  pinMode(zhong, INPUT_PULLUP)

初始化了OLED并设置了开机LOGO,这个函数updateDisplay(); 是开机LOGO的横向移动效果。
接下来是LOOP函数:

void loop() {
  if (digitalRead(zuo) == LOW) {
    currentMenu = (currentMenu == 0) ? 4 : currentMenu - 1;
    delay(200);
  }

  if (digitalRead(you) == LOW) {
    currentMenu = (currentMenu == 4) ? 0 : currentMenu + 1;
    delay(200);
  }

  // 确定当前菜单下"zhong"键的功能
  if (digitalRead(zhong) == LOW) {
    if (currentMenu == MENU_BRIGHTNESS) {
      UVLightOn = !UVLightOn; // 切换UV灯的状态
      analogWrite(UVdeng, UVLightOn ? 255 : 0); // 设置UV灯亮度
    } else if (currentMenu == MENU_TIMER && timerControlEnabled) {
       timerRunning = !timerRunning; // 切换定时器的状态
      if (timerRunning) {
        timerEndTime = millis() + ((timerHours * 60 + timerMinutes) * 60000);
        //timerRunning = true;
        digitalWrite(UVdeng, HIGH);
      } else {
        //timerRunning = false;
        digitalWrite(UVdeng, LOW);
      }
    } else if (currentMenu == MENU_AMBIENT) {
      ambientLightOn = !ambientLightOn; // 切换氛围灯的状态
      if (!ambientLightOn) {
        strip.clear();
        strip.show();
      }
    }
    delay(200);
  }
    // 处理亮度菜单
    if (currentMenu == MENU_BRIGHTNESS) {
      if(digitalRead(shang) == LOW) {
         dutyCycle += step;
        if(dutyCycle > 255) dutyCycle = 255;
           analogWrite(UVdeng, dutyCycle);
           delay(200);
    }

    if(digitalRead(xia) == LOW) {
      dutyCycle -= step;
      if(dutyCycle < 0) dutyCycle = 0;
      analogWrite(UVdeng, dutyCycle);
      delay(200);
    }
  }

//// 处理定时菜单
   if (currentMenu == MENU_TIMER && !timerRunning && timerControlEnabled) {
    if(digitalRead(shang) == LOW) {
      timerMinutes += 1;
      if (timerMinutes >= 60) {
        timerMinutes = 0;
        timerHours += 1;
      }
     /* if (timerHours >= 1) {
        timerHours = 0;
      }*/
      delay(200);
    }
    if(digitalRead(xia) == LOW) {
      timerMinutes -= 1;
      if (timerMinutes < 0) {
        timerMinutes = 59;
        timerHours -= 1;
      }
      if (timerHours < 0) {
        timerHours = 0;
        timerMinutes = 0;
      }
      delay(200);
    }
  }
//检查定时器是否到期
  if (timerRunning && millis() > timerEndTime) {
    timerRunning = false;
    digitalWrite(UVdeng, LOW);
  }
// WIFI菜单
  if (currentMenu == MENU_WIFI) {
    if (digitalRead(shang) == LOW) {
      WIFIMode();
      if (wifiConnected) {
      timeClient.update();
      }
     
      delay(200);
    }

    if (digitalRead(xia) == LOW) {
      WiFi.disconnect();
      wifiConnected = false;
      delay(200);
      }
   
  }
      // 处理氛围灯菜单
   if(currentMenu == MENU_AMBIENT){
      if (digitalRead(shang) == LOW) {
      ambientMode = (ambientMode + 1) % 8;
      setAmbientMode(ambientMode);
      delay(200);
    }

    if (digitalRead(xia) == LOW) {
      ambientBrightness += 32;
      if (ambientBrightness > 255) ambientBrightness = 32;
      strip.setBrightness(ambientBrightness);
      strip.show();
      delay(200);
       }
    }
  updateDisplay();
}

我将这五个按键分别命名为“上,下,左,右,中”目前程序:首先我设计了5个菜单分别为:亮度菜单,时间菜单,定时菜单,WIFI菜单,氛围灯菜单。





左,右按键负责切换菜单:

if (digitalRead(zuo) == LOW) {
    currentMenu = (currentMenu == 0) ? 4 : currentMenu - 1;
    delay(200);
  }

  if (digitalRead(you) == LOW) {
    currentMenu = (currentMenu == 4) ? 0 : currentMenu + 1;
    delay(200);
  }

中键在不同的菜单,代表不同功能的确认按键

// 确定当前菜单下"zhong"键的功能
  if (digitalRead(zhong) == LOW) {
    if (currentMenu == MENU_BRIGHTNESS) {
      UVLightOn = !UVLightOn; // 切换UV灯的状态
      analogWrite(UVdeng, UVLightOn ? 255 : 0); // 设置UV灯亮度
    } else if (currentMenu == MENU_TIMER && timerControlEnabled) {
       timerRunning = !timerRunning; // 切换定时器的状态
      if (timerRunning) {
        timerEndTime = millis() + ((timerHours * 60 + timerMinutes) * 60000);
        //timerRunning = true;
        digitalWrite(UVdeng, HIGH);
      } else {
        //timerRunning = false;
        digitalWrite(UVdeng, LOW);
      }
    } else if (currentMenu == MENU_AMBIENT) {
      ambientLightOn = !ambientLightOn; // 切换氛围灯的状态
      if (!ambientLightOn) {
        strip.clear();
        strip.show();
      }
    }
    delay(200);
  }

当切换到亮度菜单上,下按键负责亮度的调整,当切换到定时菜单,上,下键负责定时时长的调整。

if (currentMenu == MENU_BRIGHTNESS) {
      if(digitalRead(shang) == LOW) {
         dutyCycle += step;
        if(dutyCycle > 255) dutyCycle = 255;
           analogWrite(UVdeng, dutyCycle);
           delay(200);
    }

    if(digitalRead(xia) == LOW) {
      dutyCycle -= step;
      if(dutyCycle < 0) dutyCycle = 0;
      analogWrite(UVdeng, dutyCycle);
      delay(200);
    }
  }
 
 if (currentMenu == MENU_TIMER && !timerRunning && timerControlEnabled) {
    if(digitalRead(shang) == LOW) {
      timerMinutes += 1;
      if (timerMinutes >= 60) {
        timerMinutes = 0;
        timerHours += 1;
      }
     /* if (timerHours >= 1) {
        timerHours = 0;
      }*/
      delay(200);
    }
    if(digitalRead(xia) == LOW) {
      timerMinutes -= 1;
      if (timerMinutes < 0) {
        timerMinutes = 59;
        timerHours -= 1;
      }
      if (timerHours < 0) {
        timerHours = 0;
        timerMinutes = 0;
      }
      delay(200);
    }

其他菜单也是同理,这里就不一一列举了。

后期优化:
1.充电界面---当充电时显示充电进度;
2.电量显示界面---电池使用时显示电池消耗百分比;
3.APP控制界面---当切换为远程控制,则可以通过手机实现远程控制;

实物图






设计图

未生成预览图,请在编辑器重新保存一次

BOM

暂无BOM

附件

序号文件名称下载次数
1
202408190012_compressed.mp4
32
2
Dome1-20240505.ino
30
3
text.h
25
4
3D模型.zip
85
5
3D模型.zip
56
6
3D模型stl.zip
27
克隆工程
添加到专辑
0
0
分享
侵权投诉

工程成员

评论

全部评论(1)
按时间排序|按热度排序
粉丝0|获赞0
相关工程
暂无相关工程

底部导航