驴友花雕 发表于 2025-5-28 19:32:06

【Arduino 动手做】自由形态 LED 球形灯

本帖最后由 驴友花雕 于 2025-6-7 06:46 编辑





驴友花雕 发表于 2025-5-28 19:36:11

【Arduino 动手做】自由形态 LED 球形灯

我的圣诞项目完成了!大家都对LED立方体着迷。我本来想自己做一个,但又想让它变得更有挑战性,而且由于电路板的使用不受限制,所以我决定做一个LED球。

我花了24小时焊接,又熬了一个晚上才完成。但一切都值得。亮着的时候很美,不亮的时候更美。好好享受吧!







驴友花雕 发表于 2025-5-28 19:38:23

【Arduino 动手做】自由形态 LED 球形灯

步骤 1:观看视频!

观看 LED 球形灯的完整制作视频。它会清晰地概述整个制作过程。当然,视频中不会包含任何细节,但那样太枯燥了。详细信息就在下方页面上。观看视频并按步骤操作即可(https://www.youtube.com/watch?v=Q5d8gTppuYo)。




驴友花雕 发表于 2025-5-28 19:40:24

【Arduino 动手做】自由形态 LED 球形灯

第 2 步:收集所有工具和材料

收集所有工具和材料
工具:

窄钳
圆嘴钳
侧切钳
焊台
镊子
弯管机哈哈,不,开玩笑的。那对每个人来说都是一场表演的终结者。你只需要一些直径达到正确尺寸的圆形物体——啤酒罐、喷雾罐…​​…
3D打印机

材料:

0.8 毫米黄铜线(您也可以使用铜或钢,但它们有一些缺点)
锡焊料
助焊剂

电子元件:

带电池支架的 ESP32 开发板 - 必须与球体匹配!例如TTGO T8或FireBeetle ESP32
WS2812b RGB LED(200个)
1000mAh锂聚合物电池



驴友花雕 发表于 2025-5-28 19:42:01

【Arduino 动手做】自由形态 LED 球形灯

步骤3:模板是关键

如果没有合适的模板/夹具/模具,就不可能制作出如此复杂的形状。我设计了我的3D打印模型。模板由四个部分组成,它们由两个环连接在一起——就像一个桶。这四个部分在这里至关重要。如果用一个整体完成,最终的半球就会卡在里面。这个模板是为球体的一半设计的。不用担心,你也可以用它来制作另一半。

由于球体并非完全对称,因此这 4 个部件分为 2 种。请打印附件中每个 STL 文件各 2 个,以获得完整的模板。

附件
下载 {{ file.name }}戒指.stl下载3D视图
下载 {{ file.name }}模板A.stl下载3D视图
下载 {{ file.name }}模板B.stl下载3D视图





驴友花雕 发表于 2025-5-28 19:43:07

【Arduino 动手做】自由形态 LED 球形灯

步骤 4:从金属环开始

从金属丝环开始
这个球体由11个LED环组成,每个环由两根圆形的电线和一定数量的LED组成。完整的球体总共需要22个电线环。然而,制作起来并非那么简单。每个环的尺寸都不一样。为了方便起见,我准备了一张纸,上面画出了半球环的完整形状。虽然它并非完全准确。

记住,球体的最终形状主要取决于你在此阶段弯曲的精度。如果金属丝圈不够圆整,最终的形状可能会变形。为了得到完美的形状,我使用了金属丝弯曲机。

附件
下载 {{ file.name }}戒指.svg下载
下载 {{ file.name }}戒指.pdf下载



驴友花雕 发表于 2025-5-28 19:45:11

【Arduino 动手做】自由形态 LED 球形灯

步骤5:放置LED

将LED放入模板最底下的环中。第一个环由8个LED组成。我用黑色永久性笔从底部标记了所有LED的GND引线,以便能够识别GND(VSS)引脚的位置。将所有LED的GND引脚朝向内环,VCC引线朝向外环。这一点很重要!每个环都应完全一样!内环接地,外环供电。参见上图。插入LED之前,我已将其浸入助焊剂中,以便它们更好地固定在插槽中。

插入内环并将所有 GND 引脚焊接到内环上。插入外环并将所有 VCC 引脚焊接到外环上。VCC 引脚位于 GND 引脚的对角线处。确保外环没有连接 LED 的任何数据(DIN 和 DOUT)引脚。

重复此操作,直到获得 6 个环:

8个LED
14个LED
18个LED
20个LED
24个LED
26个LED
注意,整个球体只需要一个第六环——也就是中间的环。所以,只需将它放在两个半球中的一个即可——例如,第一个半球有5个环,第二个半球有6个环。

WS2812b LED

对于不熟悉这款 LED 的人来说,这是一款可单独寻址的RGB LED。简单来说,你只需要一根线就能点亮它,发出彩虹般的颜色。传统 LED 通过调节电流来调节亮度,而 WS2812b 则不同,它采用持续供电,并通过数字信号控制灯光。你可以告诉它要显示什么颜色。你可能会问:所以它只需要 3 根引线,对吧?第 4 根引线用于为灯链中的下一个 LED 提供控制信号。没错!这绝对是它最棒的功能。你可以拥有一长串这样的 LED,并且仍然只用一根线就能控制它们。你只需将其他 LED 的指令提供给灯链中的第一个 LED,它就会继续转发这些指令。(数据手册)











驴友花雕 发表于 2025-5-28 19:46:26

【Arduino 动手做】自由形态 LED 球形灯

步骤6:连接数据线

电源线就是线环,你已经有了。现在你还需要连接LED的另外两根引线——DIN和DOUT。DIN位于GND引脚旁边,DOUT位于VCC引脚旁边。我使用了5毫米长的电线。正确连接线环也非常重要。确保数据线没有接触到地线或电源环。确保不要将数据引脚焊接到电源环上。

从最内圈开始,一圈一圈地重复此操作。当你到达可以将最开始的LED与最后一个LED互连的位置时,就该跳到上面的一圈了。我使用了C形跳线连接两个相邻环的数据引脚。之所以需要C形,是因为你需要绕过地线和电源线。继续连接接下来的环,直到所有LED都连接成一条长蛇形。最后应该只剩下两根导线没有连接——一根在最上面的一圈。这应该是整个半球的主DIN引脚。最下面的一个是DOUT引脚。







驴友花雕 发表于 2025-5-28 19:48:21

【Arduino 动手做】自由形态 LED 球形灯

步骤7:连接电源线并重新开始

制作半球的最后一步是将环之间的电源线连接起来。这不仅是为了连接它们,也是为了创建一个将环固定在一起的结构。我再次使用了 C 形跳线来连接相邻环的电源线和地线。前三个较小的环至少使用 4 根跳线,较大的环至少使用 8 根跳线。这个数量将确保球体足够坚固。

放松时间

现在该把半球从模具中取出了!经过这么长时间的焊接,这是最放松的时刻了。首先,取下固定模具四个部分的塑料环。然后开始用力取出每个部分。动作一定要轻柔,以免弄坏任何LED!

对第一个半球满意吗?很好!现在重新开始创建第二个半球。这个半球的环数再次不同。如果你第一个半球有 6 个环,那么第二个就只有 5 个环,反之亦然。









驴友花雕 发表于 2025-5-28 19:49:44

【Arduino 动手做】自由形态 LED 球形灯

步骤8:测试半球

两个半球都准备好了吗?现在是时候测试一下它们,然后再把它们组装成球体了。我知道你很想马上完成,但相信我,你最好先测试一下。

首先,用万用表做一个简单的测试,检查电源线和地线之间是否有短路。然后将两根短线分别焊接到半球的地线和电源线上。将电源线连接到 ESP32 开发板的 3.3V 引脚,并将地线连接到地。

最后,所有 LED 都连接到一条长链上。这意味着半球也连接在一起了。取较小的一个(5 个环),将其 DIN 连接到 ESP32 开发板的 IO21 引脚,将其 DOUT 连接到另一个半球的 DIN。现在只剩下一个未连接的 LED 引脚——大球上的 DOUT。这是链中的最后一个 LED。完成了吗?启动 ESP32 开发板,并上传本教程“代码”部分的代码。

所有LED都亮了吗?太棒了!你完成了。如果没有亮,而且链条在某个地方终止了,请检查该LED是否有冷焊点或短路。我花了一个小时才修复所有错误。所以别灰心!这玩意儿做起来相当费劲!而且你还坚持到了现在!









驴友花雕 发表于 2025-5-28 19:51:18

【Arduino 动手做】自由形态 LED 球形灯

步骤9:插入微控制器和电池

取出 ESP32 开发板。先绕过开关。由于开关位于球体内部,所以无法操作。将其从开发板上拆焊下来,并焊接两根细铜线代替。让它们悬空,等微控制器放入球体后再焊接。

电路板的角落里有3到4个螺丝孔。我们不会用螺丝,而是用焊锡填充这些孔,以便以后更容易将它们连接到球形结构上——这些孔会被打磨!

取较小的半球,将微控制器放入底部较小圆环的中心,USB 接口和电池接口朝外,穿过最小的圆环。将角上的孔焊接到最小的圆环上——记住,它是接地的。将两根开关线也穿过孔,并将之前拆焊好的开关焊接到它们上面。开关有一个金属外壳,用它把它也焊接到最小的圆环上。注意不要让开关线与球体结构短路!不要让电源开关压在球体结构上——否则它会滚动。

取一根直导线,将其焊接到 ESP32 开发板上的 3.3V 引脚上。然后将另一端焊接到球体的最后一个环上——记住,这是电源。我还在接地环和开发板的 GND 引脚之间焊接了一些直导线,以加固内部结构。确保在开发板背面留出放置电池的空间!取一根铜线,将主 DIN 引脚连接到 ESP32 的 IO21 引脚。现在,半球体就搭建完成了。连接电池并进行测试。

它还能用吗?太好了!现在你可以用粘合剂把电池粘到 ESP32 开发板的背面,然后穿过孔连接。









驴友花雕 发表于 2025-5-28 19:52:42

【Arduino 动手做】自由形态 LED 球形灯

步骤10:最后焊接!

测试 ESP32 开发板上的螺丝孔是否真的接地。我需要在螺丝孔和开发板最近的 GND 引脚之间加一根短线——这样可以为第二个半球提供接地。

用短线焊接第一个半球的 DOUT 和第二个半球的 DIN(与测试阶段的操作相同)。剪下 2 毫米长的导线,并将其焊接到第二个半球最顶端的环上。我用了 8 根这样的导线。记得吗,两个最顶端的环都是电源环。所以可以安全地将每个半球最顶端的两个环焊接在一起。将半球叠放在一起,然后焊接在一起。确保两个半球之间的间隙均匀。最后,用焊锡将 ESP32 开发板的螺丝孔与最小的接地环导线连接起来。

你现在得到了一个球体!摸摸它,握住它。感觉真棒,不是吗?打开它,它能用吗?









驴友花雕 发表于 2025-5-28 19:55:10

【Arduino 动手做】自由形态 LED 球形灯

步骤11:代码

下载 Arduino IDE源代码。我使用Neopixel Bus库来控制 LED“灯带”。它有一个非常漂亮的界面,并且支持动画。

#include <NeoPixelBus.h>
#include <NeoPixelAnimator.h>

#define PIN_LEDS 21
#define NUMPIXELS 194

#define LIGHTNESS 0.05f
#define BRIGHTNESS 64

RgbColor BLACK(0, 0, 0);
RgbColor RED(BRIGHTNESS, 0, 0);
RgbColor GREEN(0, BRIGHTNESS, 0);
RgbColor BLUE(0, 0, BRIGHTNESS);
RgbColor YELLOW(BRIGHTNESS, BRIGHTNESS, 0);
RgbColor WHITE(BRIGHTNESS, BRIGHTNESS, BRIGHTNESS);

NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> leds(NUMPIXELS, PIN_LEDS);

NeoPixelAnimator animations(10); // NeoPixel animation management object

#define NUM_RINGS 11
#define RING_MAX_PIXELS 26
#define RING_MIN_PIXELS 8

// all rings starts at 0 ray
byte RINGS = {
{78, 79, 80, 81, 82, 83, 84, 77}, // 8
{65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 63, 64}, // 14
{48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 45, 46, 47}, // 18
{28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 25, 26, 27}, // 20
{5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 1, 2, 3, 4}, // 24
{102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 110, 109, 108, 107, 106, 105, 104, 103}, // 26
{127, 126, 125, 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 134, 133, 132, 131, 130, 129, 128}, // 24
{148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138, 137, 136, 135, 154, 153, 152, 151, 150, 149}, // 20
{167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 172, 171, 170, 169, 168}, // 18
{182, 181, 180, 179, 178, 177, 176, 175, 174, 173, 186, 185, 184, 183}, // 14
{192, 191, 190, 189, 188, 187, 194, 193} // 8
};

byte RING_SIZES = {8, 14, 18, 20, 24, 26, 24, 20, 18, 14, 8};

unsigned long startMillis = 0;
short animation = 0;

void setup() {
Serial.begin(115200);
initRandom();

leds.Begin();
leds.ClearTo(BLACK);
leds.Show();

//animations.StartAnimation(0, 6000, rainbowAnimation);
animations.StartAnimation(0, 2000, raysRainbow);
//animations.StartAnimation(0, 2000, ringsRainbow);
//animations.StartAnimation(0, 500, xmasOrbAnimation);
//animations.StartAnimation(0, 1000, ringAnimation);
//animations.StartAnimation(0, 500, rayAnimation);
//animations.StartAnimation(0, 100, randomAnimation);
}

void loop() {
animations.UpdateAnimations();

leds.Show();
delay(10);

if (false && (startMillis == 0 || startMillis + 20000 < millis())) { // 30s
    startMillis = millis();
    switch (animation) {
      case 0:
      animations.StartAnimation(0, 500, rayAnimation);
      break;
      case 1:
      animations.StartAnimation(0, 1000, ringAnimation);
      break;
      case 2:
      animations.StartAnimation(0, 2000, ringsRainbow);
      break;
      case 3:
      animations.StartAnimation(0, 2000, raysRainbow);
      break;
      case 4:
      animations.StartAnimation(0, 6000, rainbowAnimation);
      break;
      case 5:
      animations.StartAnimation(0, 100, randomAnimation);
      animation = -1;
      break;
    }
    animation ++;
}
}

void randomAnimation(const AnimationParam& param) {
float hue;
HslColor color;

if (param.state == AnimationState_Completed) {
    for (byte i = 0; i < 194; i ++) {
      hue = random(0, 1000) / 1000.0f;
      color = HslColor(hue, 1.0f, LIGHTNESS);
      leds.SetPixelColor(i, color);
    }

    animations.RestartAnimation(0);
}
}

void rainbowAnimation(const AnimationParam& param) {
HslColor color = HslColor(param.progress, 1.0f, LIGHTNESS);
leds.ClearTo(color);

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void raysRainbow(const AnimationParam& param) {
HslColor color;
float hue;

for (int i = 0; i < RING_MAX_PIXELS; i++) {
    hue = param.progress + (float) i / (float) RING_MAX_PIXELS;
    if (hue > 1.0f) {
      hue -= 1.0f;
    }

    color = HslColor(hue, 1.0f, LIGHTNESS);
    rayColor(i, RgbColor(color));
}

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void ringsRainbow(const AnimationParam& param) {
HslColor color;
float hue;

for (int i = 0; i < NUM_RINGS; i++) {
    hue = param.progress + (float) i / (float) NUM_RINGS;
    if (hue > 1.0f) {
      hue -= 1.0f;
    }

    color = HslColor(hue, 1.0f, LIGHTNESS);
    ringColor(i, RgbColor(color));
}

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void xmasOrbAnimation(const AnimationParam& param) {
ringColor(0, WHITE);
ringColor(1, RED);
ringColor(2, RED);
ringColor(3, RED);
ringColor(4, RED);
ringColor(5, WHITE);
ringColor(6, RED);
ringColor(7, RED);
ringColor(8, RED);
ringColor(9, RED);
ringColor(10, WHITE);

byte offset = round(param.progress);
for (byte i = offset; i < RING_SIZES; i+=2) {
    leds.SetPixelColor(RINGS - 1, WHITE);
}
for (byte i = offset; i < RING_SIZES; i+=2) {
    leds.SetPixelColor(RINGS - 1, WHITE);
}

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void ringAnimation(const AnimationParam& param) {
int index = param.progress * (NUM_RINGS * 2 - 2);

leds.ClearTo(BLACK);
if (index < NUM_RINGS) {
    ringColor(index, BLUE);
}
else {
    ringColor(NUM_RINGS - (index - NUM_RINGS) - 2, BLUE);
}

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void rayAnimation(const AnimationParam& param) {
int index = param.progress * (RING_MAX_PIXELS / 2);

if (index > 12) {
    index = 12;
}
leds.ClearTo(BLACK);
rayColor(index, BLUE);
rayColor(index + (RING_MAX_PIXELS / 2), BLUE);

if (param.state == AnimationState_Completed) {
    animations.RestartAnimation(0);
}
}

void rayColor(byte rayIndex, RgbColor color) {
int pixelIndex;
byte pixel;

if (rayIndex >= RING_MAX_PIXELS) {
    return; // prevents out of bounds
}

for (byte i = 0; i < NUM_RINGS; i ++) {
    pixelIndex = round((float) RING_SIZES / (float) RING_MAX_PIXELS * rayIndex);
    pixel = RINGS;
    if (pixel == 0) {
      continue; // skip condition
    }
    leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
}
}

void ringColor(byte ringIndex, RgbColor color) {
byte pixel;

if (ringIndex >= NUM_RINGS) {
    return; // prevents out of bounds
}

for (byte i = 0; i < RING_MAX_PIXELS; i ++) {
    pixel = RINGS;
    if (pixel == 0) {
      return; // end condition
    }
    leds.SetPixelColor(pixel - 1, color); // index starts from 1 (0 is stop condition)
}
}

void initRandom() {
// random works best with a seed that can use 31 bits
// analogRead on a unconnected pin tends toward less than four bits
uint32_t seed = analogRead(0);
delay(1);

for (int shifts = 3; shifts < 31; shifts += 3) {
    seed ^= analogRead(0) << shifts;
    delay(1);
}

// Serial.println(seed);
randomSeed(seed);
}

RgbColor colorWheel(byte wheelPos) {
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
wheelPos = 255 - wheelPos;
if (wheelPos < 85) {
    return RgbColor(255 - wheelPos * 3, 0, wheelPos * 3);
}
if (wheelPos < 170) {
    wheelPos -= 85;
    return RgbColor(0, wheelPos * 3, 255 - wheelPos * 3);
}
wheelPos -= 170;
return RgbColor(wheelPos * 3, 255 - wheelPos * 3, 0);
}

驴友花雕 发表于 2025-5-28 19:56:38

【Arduino 动手做】自由形态 LED 球形灯

为了能够控制动画,我将LED映射到11行26列的表格(数组的数组)。这样,我就能准确地知道LED在球体上的位置,并能点亮我想要的那个LED。

到目前为止我已经准备了几个动画:

垂直圆
水平圆
垂直彩虹
水平彩虹
彩虹
随机图案

注意:我必须将LED的亮度限制在满功率的20%左右。因为LED共有194个,满功率时会消耗10安培的电流。小电路板无法承受。不用担心,如果功率过大,电路板会重新启动,因此不会损坏。









驴友花雕 发表于 2025-5-28 19:58:05

【Arduino 动手做】自由形态 LED 球形灯

第 12 步:就这样!

就这样!
希望你喜欢!制作过程很有挑战性,但最终收获颇丰。这个LED球即使不亮也很漂亮。它会成为我们圣诞树上的点睛之笔!

圣诞快乐!





驴友花雕 发表于 2025-5-28 20:05:42

【Arduino 动手做】自由形态 LED 球形灯

本帖最后由 驴友花雕 于 2025-6-7 06:46 编辑

附录
项目链接:https://www.instructables.com/Christmas-LED-Sphere/
项目作者:捷克共和国 吉里普劳斯
(白天是软件开发人员,晚上是硬件制造商。)
视频教程(15分钟):https://www.youtube.com/watch?v=Q5d8gTppuYo
项目代码:https://gist.github.com/jpraus/b5084df54580ebec81fac7cec86048d3
NeoPixelBus库:https://github.com/Makuna/NeoPixelBus





驴友花雕 发表于 昨天 06:47

【Arduino 动手做】自由形态 LED 球形灯






页: [1]
查看完整版本: 【Arduino 动手做】自由形态 LED 球形灯