
1. 项目概述为什么我们需要一个“看得见”的紫外线最近几年大家对防晒和紫外线防护的意识越来越强但紫外线这东西看不见摸不着到底有多强什么时候需要加强防护光看天气预报的“紫外线指数”总觉得有点抽象。作为一个喜欢动手折腾的硬件爱好者我就在想能不能自己做一个简单、直观的装置把无形的紫外线强度变成看得见的信号这就是“自制简易紫外线检测装置”的由来。这个装置的核心目标很简单实时检测环境中的紫外线强度并用最直观的方式比如灯光颜色、声音频率或者屏幕数值反馈出来。它不追求实验室级别的精度但求成本低廉、制作简单、反馈明确适合电子爱好者、学生、甚至是对生活科技感兴趣的普通家长带着孩子一起动手。你可以用它来测试不同防晒霜的效果隔着玻璃测、比较晴天和阴天的紫外线差异或者简单地提醒自己该补涂防晒了。接下来我会从设计思路、元器件选型、电路搭建、代码编写到校准测试完整地拆解这个项目的每一步并提供我踩过的坑和总结的经验。2. 核心设计与思路拆解2.1 传感器选型为什么是GUVA-S12SD紫外线检测的核心是传感器。市面上常见的紫外线传感器主要有两种类型光电二极管型和光电导型。对于我们的简易装置光电二极管型是更合适的选择因为它响应速度快、线性度好、电路相对简单且成本较低。在众多型号中我选择了GUVA-S12SD。这是一个将紫外敏感光电二极管和信号处理IC集成在一起的小模块。选择它有几个关键理由集成度高使用简单它内部已经集成了放大和ADC模数转换电路直接通过I2C或模拟电压输出数字信号省去了我们自己设计放大滤波电路的麻烦极大降低了入门门槛。光谱响应匹配它的敏感波段主要在UV-A和UV-B波长范围约240-370nm这正是导致皮肤晒伤和老化的主要波段与我们的应用目标高度契合。供电方便工作电压在2.5V到5.5V之间与常见的Arduino、ESP8266等开发板完美兼容。成本与渠道价格亲民约十几到二十元人民币在主流电子元器件平台容易买到。注意市面上还有一种常见的紫外线传感器叫“ML8511”它输出的是模拟电压。虽然也能用但GUVA-S12SD的数字接口I2C抗干扰能力更强在长导线连接时更稳定且精度通常更高一些。对于新手我强烈推荐从数字接口的传感器入手。2.2 主控板选型Arduino Uno的稳妥之选主控板负责读取传感器数据并进行逻辑处理和输出。选择非常多从经典的Arduino Uno到更强大的ESP32。对于这个入门项目我推荐Arduino Uno或其兼容板如国产的Uno R3。为什么是Arduino Uno生态成熟有海量的库和教程遇到任何问题几乎都能找到解决方案。GUVA-S12SD就有现成的Arduino库直接调用即可。稳定性好作为教学和入门标杆Uno的硬件和IDE环境非常稳定能让你把精力集中在项目逻辑本身而不是调试开发环境。接口够用它具备I2C接口正好连接我们的数字传感器同时有多个数字IO和PWM输出可以轻松驱动LED、蜂鸣器或屏幕。编程简单基于C/C的Arduino语言易于上手逻辑清晰。当然如果你希望项目具备联网功能比如将紫外线数据上传到手机那么ESP8266如NodeMCU或ESP32是更好的选择。它们内置Wi-Fi价格也与Arduino兼容板相差无几。但在第一步我们先聚焦于核心功能的实现因此选择最稳妥的Arduino Uno。2.3 输出方案设计从简单到复杂的三种反馈检测到数据后如何让人直观感知这里设计了三种递进式的输出方案你可以根据手头材料和兴趣选择一种或组合实现。方案一三色LED指示灯最简单直观思路用一颗共阴或共阳的RGB LED通过PWM控制其颜色。映射关系低强度安全显示绿色。中等强度注意显示黄色。高强度危险显示红色。优点成本极低反应迅速一目了然。无需任何额外显示设备。方案二蜂鸣器警报听觉反馈思路用一个有源蜂鸣器根据紫外线强度改变蜂鸣频率或间隔。映射关系低强度不响或极慢速“滴…滴…滴”。中等强度中速“滴滴…滴滴…”。高强度快速连续的“滴滴滴滴”或长鸣。优点可以作为视觉的补充在你不看设备时也能提醒你。特别适合做成便携的挂件或钥匙扣。方案三OLED屏幕显示信息最全思路使用一块0.96英寸的I2C OLED屏幕直接显示紫外线指数UVI的数值和等级。显示内容实时UVI数值、强度等级低、中、高、极高、以及简单的文字提示如“请采取防护措施”。优点信息精准可读性强显得更“专业”。I2C接口的OLED屏同样接线简单。在本项目中我将以方案一三色LED结合方案三OLED屏幕为例进行讲解这样既有直观的颜色警示又有精确的数值参考。3. 核心元器件清单与电路连接3.1 物料清单在开始焊接之前请准备好以下所有元器件。大部分都能在淘宝、京东或电子市场一站式购齐。元器件名称规格/型号数量说明主控板Arduino Uno R3 (兼容版即可)1块项目大脑紫外线传感器GUVA-S12SD1个核心检测元件OLED显示屏0.96英寸 I2C接口 (SSD1306驱动)1块用于显示数值RGB LED共阴或共阳5mm直插1颗视觉警报共阴更常见电阻220Ω 或 330Ω3个用于限流保护LED面包板840孔或更多1块用于原型搭建免焊接杜邦线公对公、公对母若干建议各种长度都备一些蜂鸣器可选有源蜂鸣器 (5V)1个用于听觉警报电阻可选1kΩ1个用于蜂鸣器限流如需实操心得采购避坑指南Arduino板买兼容板没问题但务必选择CH340G芯片的它的驱动在Win10/11上安装最方便。避开那些用陌生芯片的板子。GUVA-S12SD注意区分“模块”和“裸芯片”。我们买的是模块通常是一个小电路板上面已经焊好了芯片和必要的电阻电容并引出了四根线VCC, GND, SCL, SDA。别买成需要自己设计外围电路的裸芯片。OLED屏确认是I2C接口通常是4针VCC, GND, SCL, SDA。还有一种SPI接口的针脚更多接线复杂不要买错。RGB LED共阴和共阳的接线方式相反。共阴是三个阳极长脚分别接信号阴极短脚或共用脚接地。购买时问清楚或者用万用表二极管档测试一下。3.2 电路连接详解使用面包板进行连接无需焊接方便调试。请务必在断电状态下操作。连接原理图文字描述供电部分将面包板的“正极轨”和“负极轨”分别连接到Arduino的5V和GND引脚。之后所有元件的VCC和GND都分别接到这两条轨上。GUVA-S12SD传感器VCC- 面包板正极轨 (5V)GND- 面包板负极轨 (GND)SCL- Arduino的A5引脚 (在Uno上A4是SDAA5是SCL)SDA- Arduino的A4引脚OLED屏幕 (I2C)VCC- 面包板正极轨 (5V)GND- 面包板负极轨 (GND)SCL-与传感器SCL并联一起接到Arduino的A5SDA-与传感器SDA并联一起接到Arduino的A4重要提示I2C总线是共享的多个设备可以挂载在同一对SCL/SDA线上靠不同的设备地址区分。GUVA-S12SD和SSD1306 OLED的地址不同所以可以并联。RGB LED (以共阴为例)最长脚共阴极- 面包板负极轨 (GND)另外三个脚红、绿、蓝阳极- 分别通过一个220Ω的限流电阻连接到Arduino的数字引脚 9 (蓝)、10 (绿)、11 (红)。通过PWM控制这些引脚的电压就能混合出不同颜色。蜂鸣器 (可选)**正极() ** - 通过一个1kΩ电阻连接到Arduino的数字引脚 8负极(-)- 面包板负极轨 (GND)接线完成检查清单[ ] 所有VCC是否都接到了5V[ ] 所有GND是否都接到了GND[ ] I2C设备的SCL和SDA是否分别并联并正确连接到A5和A4[ ] RGB LED的限流电阻是否已串联[ ] 检查是否有导线短路或虚接4. 代码编写与核心逻辑解析电路搭好后就是赋予它灵魂的代码部分。我们将使用Arduino IDE进行开发。首先需要安装必要的库。4.1 库文件安装与准备GUVA-S12SD库在Arduino IDE中点击「工具」-「管理库…」搜索“GUVA”通常能找到名为“GUVA-S12SD”或类似的库点击安装。OLED驱动库同样在库管理中搜索“SSD1306”选择由“Adafruit”提供的“Adafruit SSD1306”库进行安装。安装时它会提示你同时安装依赖的“Adafruit GFX Library”一并确认安装。Wire库这是Arduino自带的I2C通信库无需额外安装。4.2 代码结构与逻辑分解完整的代码较长这里我将分块解析其核心逻辑。你可以在文章末尾找到完整的可复制代码。第一部分头文件引入与对象定义#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #include “GUVA-S12SD.h” // 引入传感器库 // 定义OLED屏幕尺寸 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 // 重置引脚共享Arduino复位引脚 // 创建OLED对象 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); // 创建传感器对象 GUVA_S12SD uv_sensor; // 定义RGB LED引脚 const int redPin 11; const int greenPin 10; const int bluePin 9;这部分代码引入了所有必需的库并定义了我们要控制的硬件对象。注意GUVA-S12SD.h的引号可能需要根据你实际安装的库名稍作调整。第二部分紫外线指数计算与等级映射这是项目的核心算法。传感器读取的是原始电压或计数值我们需要将其转化为国际通用的紫外线指数。float calculateUVI(int sensorValue) { // GUVA-S12SD输出的是12位ADC值 (0-4095)对应0-5V // 首先转换为电压值 float voltage sensorValue * (5.0 / 4095.0); // 根据传感器灵敏度曲线转换电压为紫外线强度 (mW/cm²) // 注意这个转换系数需要根据传感器数据手册校准这里是一个典型值。 float uvIntensity voltage * 10.0; // 假设系数为10 // 将紫外线强度转换为紫外线指数 (UVI) // 简化公式: UVI ≈ uvIntensity * 40 float uvi uvIntensity * 40.0; return uvi; } String getUVLevel(float uvi) { if (uvi 3.0) return “Low”; else if (uvi 6.0) return “Moderate”; else if (uvi 8.0) return “High”; else if (uvi 11.0) return “Very High”; else return “Extreme”; }关键解释与校准voltage * 10.0和uvIntensity * 40.0这两个系数是示例值至关重要不同批次的传感器、不同的光照条件都会影响这个值。真正的校准需要在已知强度的紫外光源下进行。对于自制项目我们可以采用一种“相对校准法”在一个晴朗的中午将设备放在阳光下查询当天当地的官方UVI预报如气象APP然后调整代码中的系数使你的设备显示值接近官方值。这是保证读数有参考意义的关键一步。第三部分设置RGB LED颜色根据UVI等级设置LED显示不同的颜色。void setLEDColor(String level) { // 先关闭所有颜色 analogWrite(redPin, 0); analogWrite(greenPin, 0); analogWrite(bluePin, 0); if (level “Low”) { // 绿色 analogWrite(greenPin, 255); } else if (level “Moderate”) { // 黄色 红色 绿色 analogWrite(redPin, 255); analogWrite(greenPin, 100); } else if (level “High”) { // 橙色 更多红色 一些绿色 analogWrite(redPin, 255); analogWrite(greenPin, 50); } else if (level “Very High”) { // 红色 analogWrite(redPin, 255); } else { // Extreme // 闪烁红色 analogWrite(redPin, 255); delay(500); analogWrite(redPin, 0); delay(500); } }这里使用了PWM模拟输出analogWrite来混合颜色。数值范围是0-255。通过调整红绿蓝三色的比例可以得到丰富的色彩。对于“极端”等级我加入了闪烁效果以增强警示。第四部分OLED屏幕显示函数将计算出的UVI和等级美观地显示在屏幕上。void displayUVInfo(float uvi, String level) { display.clearDisplay(); // 清屏 display.setTextSize(2); // 设置字体大小 display.setTextColor(SSD1306_WHITE); // 设置颜色 display.setCursor(0, 0); // 设置起始坐标 display.print(“UVI: “); display.println(uvi, 1); // 显示UVI保留一位小数 display.setTextSize(1); display.setCursor(0, 30); display.print(“Level: “); display.println(level); // 根据等级显示建议 display.setCursor(0, 50); if (level “Low”) display.println(“Enjoy the sun!”); else if (level “Moderate”) display.println(“Hat suggested.”); else if (level “High”) display.println(“Sunscreen needed!”); else if (level “Very High”) display.println(“Seek shade!”); else display.println(“DANGER! Avoid sun!”); display.display(); // 将缓存内容刷到屏幕上 }第五部分主程序setup()和loop()这是Arduino程序的入口和循环主体。void setup() { Serial.begin(9600); // 初始化串口用于调试输出 Wire.begin(); // 初始化I2C总线 // 初始化OLED如果失败则通过串口提示 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F(“SSD1306 allocation failed”)); for(;;); // 死循环阻止程序继续 } display.display(); delay(2000); // 给屏幕一点启动时间 display.clearDisplay(); // 初始化传感器 if (!uv_sensor.begin()) { Serial.println(“Failed to find GUVA-S12SD”); while (1); } // 设置RGB LED引脚为输出模式 pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); Serial.println(“UV Detector Ready!”); } void loop() { // 1. 读取传感器原始值 int sensorValue uv_sensor.readUV(); // 使用库函数读取 // 2. 计算UVI和等级 float currentUVI calculateUVI(sensorValue); String currentLevel getUVLevel(currentUVI); // 3. 串口打印调试信息可选 Serial.print(“Raw: “); Serial.print(sensorValue); Serial.print(” UVI: “); Serial.print(currentUVI, 1); Serial.print(” Level: “); Serial.println(currentLevel); // 4. 控制LED颜色 setLEDColor(currentLevel); // 5. 在OLED上显示信息 displayUVInfo(currentUVI, currentLevel); // 6. 等待2秒后再次检测避免刷新过快 delay(2000); }在loop()函数中程序不断重复“读取-计算-输出”这个流程。delay(2000)意味着每2秒更新一次数据这个间隔对于紫外线检测来说足够实时又不会让屏幕闪烁得太厉害。5. 校准、测试与外壳制作5.1 传感器校准让读数有意义如前所述代码中的转换系数是近似值。要进行简易校准选择一个校准日找一个晴朗无云的中午。获取参考值通过权威天气应用或网站查询你所在位置当时的紫外线指数UVI。对比与调整将你的装置放在室外阳光直射下不要隔着玻璃读取串口监视器工具-串口监视器波特率9600中打印出的currentUVI值。修改代码比较你的读数和官方UVI。如果你的读数偏大就减小calculateUVI函数中的系数比如把* 40.0改成* 35.0如果偏小就增大系数。反复调整、上传代码、测试直到两者接近。多场景验证在阴凉处、室内等地方测试读数应该显著低于阳光直射下的值。这可以验证设备的相对灵敏度是正常的。注意事项真正的科学校准需要标准紫外光源和辐照计成本高昂。我们这里的“气象对标法”足以满足日常定性、半定量的检测需求让我们对紫外线强弱有一个可靠的相对比较。5.2 功能测试与调试上传代码后按以下步骤测试上电观察给Arduino上电OLED屏幕应亮起并显示初始信息RGB LED可能显示某种颜色。串口监视器打开串口监视器你应该能看到每秒打印一次的数据行。用手遮住传感器Raw值和UVI值应该下降用手机闪光灯富含蓝光有一定紫外线靠近照射数值应上升。户外测试将装置拿到窗边或户外观察数值和LED颜色变化。在阳光下UVI应显著升高LED应变为黄色或红色。OLED显示确认屏幕上的UVI数值、等级文字和建议提示都清晰可读并且随光线变化而更新。常见问题排查屏幕不亮检查接线特别是VCC和GND确认I2C地址是否正确代码中为0x3C有些屏是0x3D。传感器读数始终为0或4095检查I2C接线SCL/A5, SDA/A4确认传感器库是否正确安装尝试在setup()中加入Wire.begin()后加一小段延时。LED颜色不对确认RGB LED是共阴还是共阳代码是按共阴写的。如果是共阳需要将analogWrite的逻辑反转高电平灭低电平亮并且共阳脚接5V。数值跳变剧烈紫外线传感器本身非常敏感微小的光线变化都会引起读数波动。可以在代码中加入软件滤波比如取最近5次读数的平均值。5.3 为它安个家简易外壳制作一个裸露的面包板既不美观也不安全。我们可以用一个简单的塑料盒或3D打印外壳来包装它。材料与步骤选择容器一个大小合适的透明塑料盒如手机盒、小型收纳盒。透明盒盖方便观察OLED屏幕和LED。开孔传感器窗口在盒子侧面开一个小孔让GUVA-S12SD的感光部分露出来。可以用热熔胶或胶水将传感器模块固定在孔内侧。屏幕窗口在盒盖对应OLED屏幕的位置开一个矩形窗口。LED孔在盒盖或侧面开一个小孔让RGB LED的光透出来。电源孔为USB线开一个缺口。固定内部用尼龙扎带、双面胶或热熔胶将Arduino板、面包板固定在盒子底部防止晃动。美化可选在屏幕窗口贴上透明亚克力片防尘在盒子外面贴上标签。使用3D打印则能获得更专业的外观。你可以在Thingiverse等网站搜索“Arduino Uno Case”或“Sensor Enclosure”找到基础模型然后用建模软件如Tinkercad修改添加传感器和屏幕的开孔。6. 项目扩展与优化思路这个基础版本已经可以工作但还有很大的提升空间。这里分享几个我实践过或构思的扩展方向1. 数据记录与可视化加入SD卡模块思路添加一个SD卡模块每隔一段时间如每分钟将日期、时间和UVI值写入CSV文件。价值可以记录一整天的紫外线变化曲线分析不同时段、不同天气的紫外线强度比瞬时读数更有科研或记录价值。实现关键需要学习SD.h库的使用注意文件系统的操作打开、写入、关闭。2. 蓝牙/Wi-Fi联网与手机APP升级主控为ESP32思路将主控换成ESP32通过蓝牙BLE或Wi-Fi将数据发送到手机。价值在手机上实时查看数据设置警报阈值甚至生成历史图表。可玩性和实用性大大增强。实现关键需要学习ESP32的蓝牙或Wi-Fi编程手机端可以用现成的APP如BLE Scanner搭配通知功能或自己用MIT App Inventor等工具开发一个简易应用。3. 增加更多环境传感器打造微型气象站思路可以添加温湿度传感器如DHT22、光照强度传感器BH1750和气压计BMP280。价值不仅能测紫外线还能综合评估户外环境的舒适度与安全性。例如高温高紫外线意味着极高的晒伤和中暑风险。实现关键多数传感器也使用I2C接口可以轻松地挂载在同一条总线上只需注意地址不要冲突。代码上需要整合多个库并设计一个综合的显示界面。4. 低功耗与太阳能供电打造永久户外监测点思路使用低功耗的微控制器如ATtiny85或ESP32的深度睡眠模式搭配小型太阳能板和锂电池。价值装置可以完全无线长期放置在阳台、花园实现无人值守的持续监测。实现关键这是硬件和软件的综合挑战。需要精确计算系统功耗设计合理的睡眠/唤醒周期并选择匹配的太阳能充电管理电路。7. 常见问题与排查实录在制作和调试过程中你可能会遇到以下问题。这里是我和朋友们踩过的坑以及解决办法。问题现象可能原因排查与解决方法上电后无任何反应1. USB线或电源故障。2. Arduino板损坏。3. 电源线接反或短路。1. 换一根数据线或充电头试试。2. 观察Arduino板上的电源指示灯ON是否亮起。3. 用万用表检查面包板电源轨电压是否为5V。检查所有接线排除VCC和GND短路的可能。OLED屏幕不显示1. 屏幕供电或I2C接线错误。2. I2C地址不对。3. 库未安装或初始化失败。1. 重新检查VCC、GND、SCL、SDA四根线。2. 很多OLED屏地址是0x3C但也有0x3D。可以运行一个I2C扫描程序来确认地址。3. 检查库管理器中是否已安装Adafruit SSD1306和Adafruit GFX库。查看串口监视器是否有初始化失败的错误信息。传感器读数始终为0或固定值1. I2C通信失败。2. 传感器模块损坏。3. 库函数调用错误。1. 运行I2C扫描程序看是否能扫描到传感器的地址GUVA-S12SD通常是0x5A。2. 在setup()中检查uv_sensor.begin()的返回值如果为false则初始化失败。3. 确认你调用的读取函数名与库中一致例如readUV()还是getUV()。查看库的示例代码。RGB LED不亮或颜色异常1. 共阴/共阳类型弄错。2. 限流电阻过大或忘记接。3. 引脚定义错误。1.最常遇到的问题用万用表二极管档判断LED类型。共阴红表笔接最长脚黑表笔分别点其他脚会亮。共阳则相反。2. 确保每个颜色通道都串联了220Ω电阻直接接5V会烧毁LED。3. 核对代码中redPin,greenPin,bluePin的定义是否与实际接线一致。UVI数值明显不准1. 校准系数不适用。2. 传感器窗口被遮挡或脏污。3. 测量环境有干扰。1. 严格按照第5.1节的“气象对标法”进行校准这是提高准确性的唯一途径。2. 确保传感器表面的透明窗口清洁无胶水、灰尘覆盖。3. 避免在强烈反光如镜面、白墙附近测量这会因反射紫外光导致读数偏高。代码上传失败1. 板卡型号选错。2. 串口被占用。3. 驱动未安装。1. 在「工具」-「开发板」中确认选择了“Arduino Uno”。2. 关闭串口监视器窗口再尝试上传。3. 如果是CH340芯片的板子确保电脑已安装CH340驱动。最后一点个人体会这个项目的乐趣不仅在于做出一个能用的装置更在于整个“发现问题-解决问题”的过程。从最初的传感器选型纠结到接线时手忙脚乱再到代码调试时数值跳来跳去最后看到LED随着阳光明暗而变色那种成就感是直接购买成品无法比拟的。它让你真正理解了数据如何从物理世界被感知、转换最终成为我们可理解的信息。不妨就从今天下午开始动手试试看吧。以下为可供复制的完整代码请根据你的实际接线修改引脚定义和校准系数#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #include “GUVA-S12SD.h” // 请确保库文件名正确 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); GUVA_S12SD uv_sensor; const int redPin 11; const int greenPin 10; const int bluePin 9; float calculateUVI(int sensorValue) { float voltage sensorValue * (5.0 / 4095.0); float uvIntensity voltage * 10.0; // 【关键校准系数1】 float uvi uvIntensity * 40.0; // 【关键校准系数2】 return uvi; } String getUVLevel(float uvi) { if (uvi 3.0) return “Low”; else if (uvi 6.0) return “Moderate”; else if (uvi 8.0) return “High”; else if (uvi 11.0) return “Very High”; else return “Extreme”; } void setLEDColor(String level) { analogWrite(redPin, 0); analogWrite(greenPin, 0); analogWrite(bluePin, 0); if (level “Low”) { analogWrite(greenPin, 255); } else if (level “Moderate”) { analogWrite(redPin, 255); analogWrite(greenPin, 100); } else if (level “High”) { analogWrite(redPin, 255); analogWrite(greenPin, 50); } else if (level “Very High”) { analogWrite(redPin, 255); } else { analogWrite(redPin, 255); delay(500); analogWrite(redPin, 0); delay(500); } } void displayUVInfo(float uvi, String level) { display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.print(“UVI: “); display.println(uvi, 1); display.setTextSize(1); display.setCursor(0, 30); display.print(“Level: “); display.println(level); display.setCursor(0, 50); if (level “Low”) display.println(“Enjoy the sun!”); else if (level “Moderate”) display.println(“Hat suggested.”); else if (level “High”) display.println(“Sunscreen needed!”); else if (level “Very High”) display.println(“Seek shade!”); else display.println(“DANGER! Avoid sun!”); display.display(); } void setup() { Serial.begin(9600); Wire.begin(); if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F(“SSD1306 fail”)); for(;;); } display.display(); delay(2000); display.clearDisplay(); if (!uv_sensor.begin()) { Serial.println(“GUVA-S12SD not found”); while (1); } pinMode(redPin, OUTPUT); pinMode(greenPin, OUTPUT); pinMode(bluePin, OUTPUT); Serial.println(“UV Detector Ready!”); } void loop() { int sensorValue uv_sensor.readUV(); float currentUVI calculateUVI(sensorValue); String currentLevel getUVLevel(currentUVI); Serial.print(“Raw: “); Serial.print(sensorValue); Serial.print(” UVI: “); Serial.print(currentUVI, 1); Serial.print(” Level: “); Serial.println(currentLevel); setLEDColor(currentLevel); displayUVInfo(currentUVI, currentLevel); delay(2000); }